From 21f922a296956229a98c1ca57e2b27af40500c05 Mon Sep 17 00:00:00 2001 From: knmueller <79270349+knmueller@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:21:46 -0400 Subject: [PATCH] major: ktor 3.0.0 support - fixes #644 (#645) --- CHANGELOG.md | 3 + build.gradle.kts | 4 +- core/build.gradle.kts | 5 +- .../core/plugin/NotarizedApplication.kt | 1 - .../kompendium/core/plugin/NotarizedRoute.kt | 2 +- .../core/KompendiumAuthenticationTest.kt | 9 +- .../io/bkbn/kompendium/core/KompendiumTest.kt | 16 +-- .../kompendium/core/util/Authentication.kt | 12 +- .../bkbn/kompendium/core/util/Constraints.kt | 15 +-- .../core/util/CustomSerializableReader.kt | 8 +- .../io/bkbn/kompendium/core/util/Defaults.kt | 4 +- .../bkbn/kompendium/core/util/Enrichment.kt | 17 ++- .../kompendium/core/util/ErrorHandling.kt | 4 +- .../io/bkbn/kompendium/core/util/Examples.kt | 11 +- .../bkbn/kompendium/core/util/Exceptions.kt | 11 +- .../kompendium/core/util/Miscellaneous.kt | 23 ++-- .../kompendium/core/util/NotarizedOpenApi.kt | 36 +++-- .../core/util/PolymorphismAndGenerics.kt | 26 ++-- .../kompendium/core/util/RequiredFields.kt | 10 +- .../bkbn/kompendium/core/util/RouteParsing.kt | 13 +- .../bkbn/kompendium/core/util/TestModules.kt | 1 - .../kompendium/core/fixtures/TestHelpers.kt | 23 ++-- docs/SUMMARY.md | 1 - docs/playground.md | 1 - docs/plugins/index.md | 4 +- docs/plugins/notarized_locations.md | 62 --------- gradle.properties | 6 +- json-schema/build.gradle.kts | 6 +- locations/build.gradle.kts | 42 ------ .../locations/NotarizedLocations.kt | 85 ------------ .../locations/KompendiumLocationsTest.kt | 119 ----------------- .../kompendium/locations/util/TestModels.kt | 13 -- .../resources/T0001__simple_location.json | 92 ------------- .../resources/T0002__nested_locations.json | 124 ------------------ oas/build.gradle.kts | 3 +- playground/build.gradle.kts | 7 +- .../kompendium/playground/AuthPlayground.kt | 1 - .../playground/HiddenDocsPlayground.kt | 1 - .../playground/LocationPlayground.kt | 82 ------------ .../bkbn/kompendium/playground/util/Models.kt | 11 -- protobuf-java-converter/build.gradle.kts | 7 +- resources/build.gradle.kts | 5 +- .../resources/KompendiumResourcesTest.kt | 1 - settings.gradle.kts | 1 - 44 files changed, 151 insertions(+), 777 deletions(-) delete mode 100644 docs/plugins/notarized_locations.md delete mode 100644 locations/build.gradle.kts delete mode 100644 locations/src/main/kotlin/io/bkbn/kompendium/locations/NotarizedLocations.kt delete mode 100644 locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt delete mode 100644 locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt delete mode 100644 locations/src/test/resources/T0001__simple_location.json delete mode 100644 locations/src/test/resources/T0002__nested_locations.json delete mode 100644 playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 18f3a3b07..c8f5bc43f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Updates for Ktor 3.x support +- Remove deprecated `NotarizedLocations` + ### Added ### Changed diff --git a/build.gradle.kts b/build.gradle.kts index 00a4ed335..b51ef924d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - kotlin("jvm") version "1.9.25" apply false - kotlin("plugin.serialization") version "1.9.25" apply false + kotlin("jvm") version "2.0.21" apply false + kotlin("plugin.serialization") version "2.0.21" apply false id("io.bkbn.sourdough.library.jvm") version "0.12.2" apply false id("io.bkbn.sourdough.application.jvm") version "0.12.2" apply false id("io.bkbn.sourdough.root") version "0.12.2" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 97345bc0c..fb92bd9df 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -20,6 +20,7 @@ sourdoughLibrary { dependencies { // VERSIONS val kotestVersion: String by project + val kotlinSerializeVersion: String by project val ktorVersion: String by project val detektVersion: String by project @@ -43,7 +44,7 @@ dependencies { testFixturesApi("io.kotest:kotest-assertions-core-jvm:$kotestVersion") testFixturesApi("io.kotest:kotest-property-jvm:$kotestVersion") testFixturesApi("io.kotest:kotest-assertions-json-jvm:$kotestVersion") - testFixturesApi("io.kotest:kotest-assertions-ktor-jvm:4.4.3") + testFixturesApi("io.kotest.extensions:kotest-assertions-ktor:2.0.0") testFixturesApi("io.ktor:ktor-server-core:$ktorVersion") testFixturesApi("io.ktor:ktor-server-test-host:$ktorVersion") @@ -57,7 +58,7 @@ dependencies { testFixturesApi("dev.forst:ktor-api-key:2.2.4") - testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializeVersion") } testing { diff --git a/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedApplication.kt b/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedApplication.kt index 2b5275352..f29b27496 100644 --- a/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedApplication.kt +++ b/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedApplication.kt @@ -6,7 +6,6 @@ import io.bkbn.kompendium.json.schema.SchemaConfigurator import io.bkbn.kompendium.json.schema.definition.JsonSchema import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug import io.bkbn.kompendium.oas.OpenApiSpec -import io.ktor.server.application.call import io.ktor.server.application.createApplicationPlugin import io.ktor.server.response.respond import io.ktor.server.routing.Routing diff --git a/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedRoute.kt b/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedRoute.kt index ff693f776..a0d6e55ed 100644 --- a/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedRoute.kt +++ b/core/src/main/kotlin/io/bkbn/kompendium/core/plugin/NotarizedRoute.kt @@ -78,7 +78,7 @@ object NotarizedRoute { fun Route.calculateRoutePath() = toString() .let { - application.environment.rootPath.takeIf { root -> root.isNotEmpty() } + application.rootPath.takeIf { root -> root.isNotEmpty() } ?.let { root -> val sanitizedRoute = if (root.startsWith("/")) root else "/$root" it.replace(sanitizedRoute, "") diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumAuthenticationTest.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumAuthenticationTest.kt index 940796902..c54b612bc 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumAuthenticationTest.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumAuthenticationTest.kt @@ -18,6 +18,7 @@ import io.ktor.http.HttpMethod import io.ktor.server.application.install import io.ktor.server.auth.Authentication import io.ktor.server.auth.OAuthServerSettings +import io.ktor.server.auth.Principal import io.ktor.server.auth.UserIdPrincipal import io.ktor.server.auth.basic import io.ktor.server.auth.jwt.jwt @@ -92,14 +93,18 @@ class KompendiumAuthenticationTest : DescribeSpec({ ) { customAuthConfig() } } it("Can provide multiple authentication strategies") { + data class TestAppPrincipal(val key: String) : Principal TestHelpers.openApiTestAllSerializers( snapshotName = "T0047__multiple_auth_strategies.json", applicationSetup = { install(Authentication) { apiKey("api-key") { headerName = "X-API-KEY" - validate { - UserIdPrincipal("Placeholder") + validate { key -> + // api key library (dev.forst.ktor.apikey) is using the deprecated `Principal` class + key + .takeIf { it == "api-key" } + ?.let { TestAppPrincipal(it) } } } jwt("jwt") { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt index 072f58002..5650751b5 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/KompendiumTest.kt @@ -38,7 +38,6 @@ import io.bkbn.kompendium.oas.security.BasicAuth import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule import io.kotest.core.spec.style.DescribeSpec import io.ktor.serialization.kotlinx.json.json -import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.auth.Authentication import io.ktor.server.auth.UserIdPrincipal @@ -182,13 +181,16 @@ class KompendiumTest : DescribeSpec({ it("Can generate paths without application root-path") { openApiTestAllSerializers( "T0054__app_with_rootpath.json", - applicationEnvironmentBuilder = { + applicationSetup = { rootPath = "/example" }, specOverrides = { copy( servers = servers.map { it.copy(url = URI("${it.url}/example")) }.toMutableList() ) + }, + serverConfigSetup = { + rootPath = "/example" } ) { notarizedGet() } } @@ -202,12 +204,10 @@ class KompendiumTest : DescribeSpec({ snapshotName = "T0072__custom_serialization_strategy.json", notarizedApplicationConfigOverrides = { specRoute = { spec, routing -> - routing { - route("/openapi.json") { - get { - call.response.headers.append("Content-Type", "application/json") - call.respondText { customJsonEncoder.encodeToString(spec) } - } + routing.route("/openapi.json") { + get { + call.response.headers.append("Content-Type", "application/json") + call.respondText { customJsonEncoder.encodeToString(spec) } } } } diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Authentication.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Authentication.kt index be79d7e67..ac79341b3 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Authentication.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Authentication.kt @@ -9,13 +9,11 @@ import io.bkbn.kompendium.core.util.TestModules.defaultPathSummary import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription import io.bkbn.kompendium.core.util.TestModules.rootPath import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install import io.ktor.server.auth.authenticate -import io.ktor.server.routing.Routing -import io.ktor.server.routing.post +import io.ktor.server.routing.Route import io.ktor.server.routing.route -fun Routing.defaultAuthConfig() { +fun Route.defaultAuthConfig() { authenticate("basic") { route(rootPath) { basicGetGenerator() @@ -23,7 +21,7 @@ fun Routing.defaultAuthConfig() { } } -fun Routing.customAuthConfig() { +fun Route.customAuthConfig() { authenticate("auth-oauth-google") { route(rootPath) { install(NotarizedRoute()) { @@ -44,7 +42,7 @@ fun Routing.customAuthConfig() { } } -fun Routing.customScopesOnSiblingPathOperations() { +fun Route.customScopesOnSiblingPathOperations() { authenticate("auth-oauth-google") { route(rootPath) { install(NotarizedRoute()) { @@ -81,7 +79,7 @@ fun Routing.customScopesOnSiblingPathOperations() { } } -fun Routing.multipleAuthStrategies() { +fun Route.multipleAuthStrategies() { authenticate("jwt", "api-key") { route(rootPath) { basicGetGenerator() diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt index fd6ebf6d4..edd0b05ad 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Constraints.kt @@ -12,11 +12,10 @@ import io.bkbn.kompendium.enrichment.NumberEnrichment import io.bkbn.kompendium.enrichment.ObjectEnrichment import io.bkbn.kompendium.enrichment.StringEnrichment import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.route -fun Routing.intConstraints() { +fun Route.intConstraints() { route(defaultPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -43,7 +42,7 @@ fun Routing.intConstraints() { } } -fun Routing.doubleConstraints() { +fun Route.doubleConstraints() { route(defaultPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -70,7 +69,7 @@ fun Routing.doubleConstraints() { } } -fun Routing.stringConstraints() { +fun Route.stringConstraints() { route(defaultPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -96,7 +95,7 @@ fun Routing.stringConstraints() { } } -fun Routing.stringPatternConstraints() { +fun Route.stringPatternConstraints() { route(defaultPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -121,7 +120,7 @@ fun Routing.stringPatternConstraints() { } } -fun Routing.stringContentEncodingConstraints() { +fun Route.stringContentEncodingConstraints() { route(defaultPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -147,7 +146,7 @@ fun Routing.stringContentEncodingConstraints() { } } -fun Routing.arrayConstraints() { +fun Route.arrayConstraints() { route(defaultPath) { install(NotarizedRoute()) { get = GetInfo.builder { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/CustomSerializableReader.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/CustomSerializableReader.kt index ddc8150e9..bd1cdb458 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/CustomSerializableReader.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/CustomSerializableReader.kt @@ -3,8 +3,8 @@ package io.bkbn.kompendium.core.util import io.bkbn.kompendium.core.fixtures.SerialNameObject import io.bkbn.kompendium.core.fixtures.TransientObject import io.bkbn.kompendium.core.fixtures.UnbackedObject -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route -fun Routing.ignoredFieldsResponse() = basicGetGenerator() -fun Routing.unbackedFieldsResponse() = basicGetGenerator() -fun Routing.customFieldNameResponse() = basicGetGenerator() +fun Route.ignoredFieldsResponse() = basicGetGenerator() +fun Route.unbackedFieldsResponse() = basicGetGenerator() +fun Route.customFieldNameResponse() = basicGetGenerator() diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Defaults.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Defaults.kt index 71aa4513e..a40a72d1b 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Defaults.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Defaults.kt @@ -3,9 +3,9 @@ package io.bkbn.kompendium.core.util import io.bkbn.kompendium.core.fixtures.TestResponse import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route -fun Routing.defaultParameter() = basicGetGenerator( +fun Route.defaultParameter() = basicGetGenerator( params = listOf( Parameter( name = "id", diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Enrichment.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Enrichment.kt index d9bf102a1..a1fcd7cda 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Enrichment.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Enrichment.kt @@ -16,11 +16,10 @@ import io.bkbn.kompendium.enrichment.NumberEnrichment import io.bkbn.kompendium.enrichment.ObjectEnrichment import io.bkbn.kompendium.enrichment.StringEnrichment import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.route -fun Routing.enrichedSimpleResponse() { +fun Route.enrichedSimpleResponse() { route("/enriched") { install(NotarizedRoute()) { get = GetInfo.builder { @@ -44,7 +43,7 @@ fun Routing.enrichedSimpleResponse() { } } -fun Routing.enrichedSimpleRequest() { +fun Route.enrichedSimpleRequest() { route("/example") { install(NotarizedRoute()) { parameters = TestModules.defaultParams @@ -78,7 +77,7 @@ fun Routing.enrichedSimpleRequest() { } } -fun Routing.enrichedNestedCollection() { +fun Route.enrichedNestedCollection() { route("/example") { install(NotarizedRoute()) { parameters = TestModules.defaultParams @@ -114,7 +113,7 @@ fun Routing.enrichedNestedCollection() { } } -fun Routing.enrichedTopLevelCollection() { +fun Route.enrichedTopLevelCollection() { route("/example") { install(NotarizedRoute()) { parameters = TestModules.defaultParams @@ -150,7 +149,7 @@ fun Routing.enrichedTopLevelCollection() { } } -fun Routing.enrichedComplexGenericType() { +fun Route.enrichedComplexGenericType() { route("/example") { install(NotarizedRoute()) { parameters = TestModules.defaultParams @@ -193,7 +192,7 @@ fun Routing.enrichedComplexGenericType() { } } -fun Routing.enrichedGenericResponse() { +fun Route.enrichedGenericResponse() { route("/example") { install(NotarizedRoute()) { get = GetInfo.builder { @@ -228,7 +227,7 @@ fun Routing.enrichedGenericResponse() { } } -fun Routing.enrichedMap() { +fun Route.enrichedMap() { route("/example") { install(NotarizedRoute()) { get = GetInfo.builder { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/ErrorHandling.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/ErrorHandling.kt index f3b5b3c19..57becf7db 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/ErrorHandling.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/ErrorHandling.kt @@ -3,10 +3,10 @@ package io.bkbn.kompendium.core.util import io.bkbn.kompendium.core.fixtures.TestResponse import io.bkbn.kompendium.core.util.TestModules.defaultPath import io.ktor.server.auth.authenticate -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.route -fun Routing.samePathSameMethod() { +fun Route.samePathSameMethod() { route(defaultPath) { basicGetGenerator() authenticate("basic") { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Examples.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Examples.kt index 132bd2965..49da73cf4 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Examples.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Examples.kt @@ -14,11 +14,10 @@ import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.MediaType import io.bkbn.kompendium.oas.payload.Parameter import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.route -fun Routing.reqRespExamples() { +fun Route.reqRespExamples() { route(rootPath) { install(NotarizedRoute()) { post = PostInfo.builder { @@ -44,7 +43,7 @@ fun Routing.reqRespExamples() { } } -fun Routing.exampleParams() = basicGetGenerator( +fun Route.exampleParams() = basicGetGenerator( params = listOf( Parameter( name = "id", @@ -57,7 +56,7 @@ fun Routing.exampleParams() = basicGetGenerator( ) ) -fun Routing.optionalReqExample() { +fun Route.optionalReqExample() { route(rootPath) { install(NotarizedRoute()) { post = PostInfo.builder { @@ -84,7 +83,7 @@ fun Routing.optionalReqExample() { } } -fun Routing.exampleSummaryAndDescription() { +fun Route.exampleSummaryAndDescription() { route(rootPath) { install(NotarizedRoute()) { post = PostInfo.builder { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Exceptions.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Exceptions.kt index 796a556db..0b30f1978 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Exceptions.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Exceptions.kt @@ -11,11 +11,10 @@ import io.bkbn.kompendium.core.util.TestModules.defaultPathSummary import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription import io.bkbn.kompendium.core.util.TestModules.rootPath import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.route -fun Routing.singleException() { +fun Route.singleException() { route(rootPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -36,7 +35,7 @@ fun Routing.singleException() { } } -fun Routing.multipleExceptions() { +fun Route.multipleExceptions() { route(rootPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -62,7 +61,7 @@ fun Routing.multipleExceptions() { } } -fun Routing.polymorphicException() { +fun Route.polymorphicException() { route(rootPath) { install(NotarizedRoute()) { get = GetInfo.builder { @@ -83,7 +82,7 @@ fun Routing.polymorphicException() { } } -fun Routing.genericException() { +fun Route.genericException() { route(rootPath) { install(NotarizedRoute()) { get = GetInfo.builder { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Miscellaneous.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Miscellaneous.kt index 17a44c74d..43b7eacda 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/Miscellaneous.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/Miscellaneous.kt @@ -21,17 +21,16 @@ import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install import io.ktor.server.auth.authenticate -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.route -fun Routing.withOperationId() = basicGetGenerator(operationId = "getThisDude") -fun Routing.nullableNestedObject() = basicGetGenerator() -fun Routing.nullableEnumField() = basicGetGenerator() -fun Routing.nullableReference() = basicGetGenerator() -fun Routing.dateTimeString() = basicGetGenerator() -fun Routing.headerParameter() = basicGetGenerator( +fun Route.withOperationId() = basicGetGenerator(operationId = "getThisDude") +fun Route.nullableNestedObject() = basicGetGenerator() +fun Route.nullableEnumField() = basicGetGenerator() +fun Route.nullableReference() = basicGetGenerator() +fun Route.dateTimeString() = basicGetGenerator() +fun Route.headerParameter() = basicGetGenerator( params = listOf( Parameter( name = "X-User-Email", @@ -42,10 +41,10 @@ fun Routing.headerParameter() = basicGetGenerator( ) ) -fun Routing.nestedTypeName() = basicGetGenerator() -fun Routing.topLevelNullable() = basicGetGenerator() -fun Routing.simpleRecursive() = basicGetGenerator() -fun Routing.samePathDifferentMethodsAndAuth() { +fun Route.nestedTypeName() = basicGetGenerator() +fun Route.topLevelNullable() = basicGetGenerator() +fun Route.simpleRecursive() = basicGetGenerator() +fun Route.samePathDifferentMethodsAndAuth() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/NotarizedOpenApi.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/NotarizedOpenApi.kt index 4c2c9073d..4c1e94e99 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/NotarizedOpenApi.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/NotarizedOpenApi.kt @@ -26,11 +26,9 @@ import io.bkbn.kompendium.oas.payload.Header import io.bkbn.kompendium.oas.payload.Parameter import io.ktor.http.HttpHeaders import io.ktor.http.HttpStatusCode -import io.ktor.server.application.call -import io.ktor.server.application.install import io.ktor.server.response.respond import io.ktor.server.response.respondText -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.delete import io.ktor.server.routing.get import io.ktor.server.routing.head @@ -40,7 +38,7 @@ import io.ktor.server.routing.post import io.ktor.server.routing.put import io.ktor.server.routing.route -fun Routing.notarizedGet() { +fun Route.notarizedGet() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -60,7 +58,7 @@ fun Routing.notarizedGet() { } } -fun Routing.responseHeaders() { +fun Route.responseHeaders() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -92,7 +90,7 @@ fun Routing.responseHeaders() { } } -fun Routing.notarizedPost() { +fun Route.notarizedPost() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -116,7 +114,7 @@ fun Routing.notarizedPost() { } } -fun Routing.notarizedPut() { +fun Route.notarizedPut() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -140,7 +138,7 @@ fun Routing.notarizedPut() { } } -fun Routing.notarizedDelete() { +fun Route.notarizedDelete() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -160,7 +158,7 @@ fun Routing.notarizedDelete() { } } -fun Routing.notarizedPatch() { +fun Route.notarizedPatch() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -184,7 +182,7 @@ fun Routing.notarizedPatch() { } } -fun Routing.notarizedHead() { +fun Route.notarizedHead() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -205,7 +203,7 @@ fun Routing.notarizedHead() { } } -fun Routing.notarizedOptions() { +fun Route.notarizedOptions() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -225,7 +223,7 @@ fun Routing.notarizedOptions() { } } -fun Routing.complexRequest() { +fun Route.complexRequest() { route(rootPath) { install(NotarizedRoute()) { put = PutInfo.builder { @@ -248,7 +246,7 @@ fun Routing.complexRequest() { } } -fun Routing.primitives() { +fun Route.primitives() { route(rootPath) { install(NotarizedRoute()) { put = PutInfo.builder { @@ -268,7 +266,7 @@ fun Routing.primitives() { } } -fun Routing.returnsList() { +fun Route.returnsList() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -285,7 +283,7 @@ fun Routing.returnsList() { } } -fun Routing.returnsEnumList() { +fun Route.returnsEnumList() { route(defaultPath) { install(NotarizedRoute()) { parameters = defaultParams @@ -302,7 +300,7 @@ fun Routing.returnsEnumList() { } } -fun Routing.nonRequiredParams() { +fun Route.nonRequiredParams() { route("/optional") { install(NotarizedRoute()) { parameters = listOf( @@ -331,7 +329,7 @@ fun Routing.nonRequiredParams() { } } -fun Routing.overrideMediaTypes() { +fun Route.overrideMediaTypes() { route("/media_types") { install(NotarizedRoute()) { put = PutInfo.builder { @@ -353,7 +351,7 @@ fun Routing.overrideMediaTypes() { } } -fun Routing.postNoReqBody() { +fun Route.postNoReqBody() { route("/no_req_body") { install(NotarizedRoute()) { post = PostInfo.builder { @@ -369,7 +367,7 @@ fun Routing.postNoReqBody() { } } -fun Routing.fieldOutsideConstructor() { +fun Route.fieldOutsideConstructor() { route("/field_outside_constructor") { install(NotarizedRoute()) { post = PostInfo.builder { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/PolymorphismAndGenerics.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/PolymorphismAndGenerics.kt index 7c9b42733..8bb3e9867 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/PolymorphismAndGenerics.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/PolymorphismAndGenerics.kt @@ -10,17 +10,17 @@ import io.bkbn.kompendium.core.fixtures.Gibbity import io.bkbn.kompendium.core.fixtures.Gizmo import io.bkbn.kompendium.core.fixtures.MultiNestedGenerics import io.bkbn.kompendium.core.fixtures.Page -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route -fun Routing.polymorphicResponse() = basicGetGenerator() -fun Routing.polymorphicCollectionResponse() = basicGetGenerator>() -fun Routing.polymorphicMapResponse() = basicGetGenerator>() -fun Routing.simpleGenericResponse() = basicGetGenerator>() -fun Routing.gnarlyGenericResponse() = basicGetGenerator, String>>() -fun Routing.nestedGenericResponse() = basicGetGenerator>>() -fun Routing.genericPolymorphicResponse() = basicGetGenerator>() -fun Routing.genericPolymorphicResponseMultipleImpls() = basicGetGenerator>() -fun Routing.nestedGenericCollection() = basicGetGenerator>() -fun Routing.nestedGenericMultipleParamsCollection() = basicGetGenerator>() -fun Routing.overrideSealedTypeIdentifier() = basicGetGenerator() -fun Routing.subtypeNotCompleteSetOfParentProperties() = basicGetGenerator() +fun Route.polymorphicResponse() = basicGetGenerator() +fun Route.polymorphicCollectionResponse() = basicGetGenerator>() +fun Route.polymorphicMapResponse() = basicGetGenerator>() +fun Route.simpleGenericResponse() = basicGetGenerator>() +fun Route.gnarlyGenericResponse() = basicGetGenerator, String>>() +fun Route.nestedGenericResponse() = basicGetGenerator>>() +fun Route.genericPolymorphicResponse() = basicGetGenerator>() +fun Route.genericPolymorphicResponseMultipleImpls() = basicGetGenerator>() +fun Route.nestedGenericCollection() = basicGetGenerator>() +fun Route.nestedGenericMultipleParamsCollection() = basicGetGenerator>() +fun Route.overrideSealedTypeIdentifier() = basicGetGenerator() +fun Route.subtypeNotCompleteSetOfParentProperties() = basicGetGenerator() diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/RequiredFields.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/RequiredFields.kt index 6ae24cd3f..110cfb331 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/RequiredFields.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/RequiredFields.kt @@ -5,9 +5,9 @@ import io.bkbn.kompendium.core.fixtures.NullableField import io.bkbn.kompendium.core.fixtures.TestResponse import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route -fun Routing.requiredParams() = basicGetGenerator( +fun Route.requiredParams() = basicGetGenerator( params = listOf( Parameter( name = "id", @@ -17,7 +17,7 @@ fun Routing.requiredParams() = basicGetGenerator( ) ) -fun Routing.nonRequiredParam() = basicGetGenerator( +fun Route.nonRequiredParam() = basicGetGenerator( params = listOf( Parameter( name = "id", @@ -28,5 +28,5 @@ fun Routing.nonRequiredParam() = basicGetGenerator( ) ) -fun Routing.defaultField() = basicGetGenerator() -fun Routing.nullableField() = basicGetGenerator() +fun Route.defaultField() = basicGetGenerator() +fun Route.nullableField() = basicGetGenerator() diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/RouteParsing.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/RouteParsing.kt index a277517f4..464d1c391 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/RouteParsing.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/RouteParsing.kt @@ -11,12 +11,11 @@ import io.bkbn.kompendium.core.util.TestModules.rootPath import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.routing.route import io.ktor.server.routing.param -fun Routing.simplePathParsing() { +fun Route.simplePathParsing() { route("/this") { route("/is") { route("/a") { @@ -49,7 +48,7 @@ fun Routing.simplePathParsing() { } } -fun Routing.rootRoute() { +fun Route.rootRoute() { route(rootPath) { install(NotarizedRoute()) { parameters = listOf(defaultParams.last()) @@ -66,7 +65,7 @@ fun Routing.rootRoute() { } } -fun Routing.nestedUnderRoot() { +fun Route.nestedUnderRoot() { route("/") { route("/testerino") { install(NotarizedRoute()) { @@ -84,7 +83,7 @@ fun Routing.nestedUnderRoot() { } } -fun Routing.trailingSlash() { +fun Route.trailingSlash() { route("/test") { route("/") { install(NotarizedRoute()) { @@ -102,7 +101,7 @@ fun Routing.trailingSlash() { } } -fun Routing.paramWrapper() { +fun Route.paramWrapper() { route("/test") { param("a") { param("b") { diff --git a/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt b/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt index 77b6513b8..d1c46db41 100644 --- a/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt +++ b/core/src/test/kotlin/io/bkbn/kompendium/core/util/TestModules.kt @@ -9,7 +9,6 @@ import io.bkbn.kompendium.core.util.TestModules.rootPath import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter import io.ktor.http.HttpStatusCode -import io.ktor.server.application.install import io.ktor.server.routing.Route import io.ktor.server.routing.Routing import io.ktor.server.routing.route diff --git a/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt b/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt index d5fae5f37..4827ccb07 100644 --- a/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt +++ b/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt @@ -17,10 +17,11 @@ import io.ktor.client.statement.bodyAsText import io.ktor.http.HttpStatusCode import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application -import io.ktor.server.engine.ApplicationEngineEnvironmentBuilder +import io.ktor.server.application.ServerConfigBuilder +import io.ktor.server.engine.ApplicationEnvironmentBuilder import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.plugins.contentnegotiation.ContentNegotiationConfig -import io.ktor.server.routing.Routing +import io.ktor.server.routing.Route import io.ktor.server.testing.ApplicationTestBuilder import io.ktor.server.testing.testApplication import kotlinx.serialization.json.Json @@ -59,7 +60,7 @@ object TestHelpers { customTypes: Map = emptyMap(), applicationSetup: Application.() -> Unit = { }, specOverrides: OpenApiSpec.() -> OpenApiSpec = { this }, - applicationEnvironmentBuilder: ApplicationEngineEnvironmentBuilder.() -> Unit = {}, + applicationEnvironmentBuilder: ApplicationEnvironmentBuilder.() -> Unit = {}, notarizedApplicationConfigOverrides: NotarizedApplication.Config.() -> Unit = {}, contentNegotiation: ContentNegotiationConfig.() -> Unit = { json(Json { @@ -68,7 +69,9 @@ object TestHelpers { serializersModule = KompendiumSerializersModule.module }) }, - routeUnderTest: Routing.() -> Unit + + serverConfigSetup: ServerConfigBuilder.() -> Unit = { }, + routeUnderTest: Route.() -> Unit ) { openApiTest( snapshotName, @@ -78,19 +81,21 @@ object TestHelpers { customTypes, notarizedApplicationConfigOverrides, contentNegotiation, - applicationEnvironmentBuilder + applicationEnvironmentBuilder, + serverConfigSetup ) } private fun openApiTest( snapshotName: String, - routeUnderTest: Routing.() -> Unit, + routeUnderTest: Route.() -> Unit, applicationSetup: Application.() -> Unit, specOverrides: OpenApiSpec.() -> OpenApiSpec, typeOverrides: Map = emptyMap(), notarizedApplicationConfigOverrides: NotarizedApplication.Config.() -> Unit, contentNegotiation: ContentNegotiationConfig.() -> Unit, - applicationBuilder: ApplicationEngineEnvironmentBuilder.() -> Unit + applicationBuilder: ApplicationEnvironmentBuilder.() -> Unit, + serverConfigSetup: ServerConfigBuilder.() -> Unit ) = testApplication { environment(applicationBuilder) install(NotarizedApplication()) { @@ -103,12 +108,14 @@ object TestHelpers { contentNegotiation() } application(applicationSetup) + serverConfig(serverConfigSetup) routing { swagger() redoc() routeUnderTest() } - val root = ApplicationEngineEnvironmentBuilder().apply(applicationBuilder).rootPath + + val root = ServerConfigBuilder(ApplicationEnvironmentBuilder().apply(applicationBuilder).build()).apply(serverConfigSetup).rootPath compareOpenAPISpec(root, snapshotName) } } diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 5280d3669..81c92d205 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -4,7 +4,6 @@ * [Plugins](plugins/index.md) * [Notarized Application](plugins/notarized_application.md) * [Notarized Route](plugins/notarized_route.md) - * [Notarized Locations](plugins/notarized_locations.md) * [Notarized Resources](plugins/notarized_resources.md) * [Concepts](concepts/index.md) * [Enrichment](concepts/enrichment.md) diff --git a/docs/playground.md b/docs/playground.md index 5c75f295c..c970fffd2 100644 --- a/docs/playground.md +++ b/docs/playground.md @@ -12,7 +12,6 @@ At the moment, the following playground applications are | Gson | Serialization using Gson instead of the default Kotlinx | | Hidden Docs | Place your generated documentation behind authorization | | Jackson | Serialization using Jackson instead of the default KotlinX | -| Locations | Using the Ktor Locations API to define routes | | Resources | Using the Ktor Resources API to define routes | You can find all of the playground diff --git a/docs/plugins/index.md b/docs/plugins/index.md index 05eaec31c..36c025913 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -7,5 +7,5 @@ From there, a `NotarizedRoute` plugin is attached to each route you wish to docu be an iterative process. Each route you notarize will be picked up and injected into the OpenAPI spec that Kompendium generates for you. -Finally, there is the `NotarizedLocations` plugin that allows you to leverage and document your usage of the -Ktor [Locations](https://ktor.io/docs/locations.html) API. +Finally, there is the `NotarizedResources` plugin that allows you to leverage and document your usage of the +Ktor [Resources](https://ktor.io/docs/server-resources.html) API. diff --git a/docs/plugins/notarized_locations.md b/docs/plugins/notarized_locations.md deleted file mode 100644 index bc354cd86..000000000 --- a/docs/plugins/notarized_locations.md +++ /dev/null @@ -1,62 +0,0 @@ -The Ktor Locations API is an experimental API that allows users to add increased type safety to their defined routes. - -You can read more about it [here](https://ktor.io/docs/locations.html). - -Kompendium supports Locations through an ancillary module `kompendium-locations` - -## Adding the Artifact - -Prior to documenting your locations, you will need to add the artifact to your gradle build file. - -```kotlin -dependencies { - implementation("io.bkbn:kompendium-locations:latest.release") -} -``` - -## Installing Plugin - -Once you have installed the dependency, you can install the plugin. The `NotarizedLocations` plugin is an _application_ -level plugin, and **must** be install after both the `NotarizedApplication` plugin and the Ktor `Locations` plugin. - -```kotlin -private fun Application.mainModule() { - install(Locations) - install(NotarizedApplication()) { - spec = baseSpec - } - install(NotarizedLocations()) { - locations = mapOf( - Listing::class to NotarizedLocations.LocationMetadata( - parameters = listOf( - Parameter( - name = "name", - `in` = Parameter.Location.path, - schema = TypeDefinition.STRING - ), - Parameter( - name = "page", - `in` = Parameter.Location.path, - schema = TypeDefinition.INT - ) - ), - get = GetInfo.builder { - summary("Get user by id") - description("A very neat endpoint!") - response { - responseCode(HttpStatusCode.OK) - responseType() - description("Will return whether or not the user is real 😱") - } - } - ), - ) - } -} -``` - -Here, the `locations` property is a map of `KClass<*>` to metadata describing that locations metadata. This -metadata is functionally identical to how a standard `NotarizedRoute` is defined. - -> ⚠️ If you try to map a class that is not annotated with the ktor `@Location` annotation, you will get a runtime -> exception! diff --git a/gradle.properties b/gradle.properties index f1204d8b1..02c934e72 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,8 @@ org.gradle.jvmargs=-Xmx2000m org.gradle.parallel=true # Dependencies -ktorVersion=2.3.12 -kotestVersion=5.9.1 +kotlinVersion=2.0.21 +kotlinSerializeVersion=1.7.+ +ktorVersion=3.0.0 +kotestVersion=6.0.0.M1 detektVersion=1.23.7 diff --git a/json-schema/build.gradle.kts b/json-schema/build.gradle.kts index ffe281585..9f90d9f91 100644 --- a/json-schema/build.gradle.kts +++ b/json-schema/build.gradle.kts @@ -19,12 +19,14 @@ sourdoughLibrary { dependencies { // Versions val detektVersion: String by project + val kotlinVersion: String by project + val kotlinSerializeVersion: String by project // Kompendium api(projects.kompendiumEnrichment) - implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.25") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializeVersion") // Formatting detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion") diff --git a/locations/build.gradle.kts b/locations/build.gradle.kts deleted file mode 100644 index abdcb4818..000000000 --- a/locations/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -plugins { - kotlin("jvm") - id("io.bkbn.sourdough.library.jvm") - id("io.gitlab.arturbosch.detekt") - id("com.adarshr.test-logger") - id("maven-publish") - id("java-library") - id("signing") - id("org.jetbrains.kotlinx.kover") -} - -sourdoughLibrary { - libraryName.set("Kompendium Locations") - libraryDescription.set("Supplemental library for Kompendium offering support for Ktor's Location API") - compilerArgs.set(listOf("-opt-in=kotlin.RequiresOptIn")) -} - -dependencies { - // Versions - val detektVersion: String by project - - // IMPLEMENTATION - - implementation(projects.kompendiumCore) - implementation("io.ktor:ktor-server-core:2.3.12") - implementation("io.ktor:ktor-server-locations:2.3.12") - - // TESTING - - testImplementation(testFixtures(projects.kompendiumCore)) - - // Formatting - detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion") -} - -testing { - suites { - named("test", JvmTestSuite::class) { - useJUnitJupiter() - } - } -} diff --git a/locations/src/main/kotlin/io/bkbn/kompendium/locations/NotarizedLocations.kt b/locations/src/main/kotlin/io/bkbn/kompendium/locations/NotarizedLocations.kt deleted file mode 100644 index c0bb19998..000000000 --- a/locations/src/main/kotlin/io/bkbn/kompendium/locations/NotarizedLocations.kt +++ /dev/null @@ -1,85 +0,0 @@ -package io.bkbn.kompendium.locations - -import io.bkbn.kompendium.core.attribute.KompendiumAttributes -import io.bkbn.kompendium.core.metadata.DeleteInfo -import io.bkbn.kompendium.core.metadata.GetInfo -import io.bkbn.kompendium.core.metadata.HeadInfo -import io.bkbn.kompendium.core.metadata.OptionsInfo -import io.bkbn.kompendium.core.metadata.PatchInfo -import io.bkbn.kompendium.core.metadata.PostInfo -import io.bkbn.kompendium.core.metadata.PutInfo -import io.bkbn.kompendium.core.util.Helpers.addToSpec -import io.bkbn.kompendium.core.util.SpecConfig -import io.bkbn.kompendium.oas.path.Path -import io.bkbn.kompendium.oas.payload.Parameter -import io.ktor.server.application.createApplicationPlugin -import io.ktor.server.locations.KtorExperimentalLocationsAPI -import io.ktor.server.locations.Location -import kotlin.reflect.KClass -import kotlin.reflect.full.findAnnotation -import kotlin.reflect.full.hasAnnotation -import kotlin.reflect.full.memberProperties - -@Deprecated( - message = "This functionality is deprecated and will be removed in the future. " + - "Use 'ktor-server-resources' with 'kompendium-resources' plugin instead.", - level = DeprecationLevel.WARNING -) -object NotarizedLocations { - - data class LocationMetadata( - override var tags: Set = emptySet(), - override var parameters: List = emptyList(), - override var get: GetInfo? = null, - override var post: PostInfo? = null, - override var put: PutInfo? = null, - override var delete: DeleteInfo? = null, - override var patch: PatchInfo? = null, - override var head: HeadInfo? = null, - override var options: OptionsInfo? = null, - override var security: Map>? = null, - ) : SpecConfig - - class Config { - lateinit var locations: Map, LocationMetadata> - } - - operator fun invoke() = createApplicationPlugin( - name = "NotarizedLocations", - createConfiguration = ::Config - ) { - val spec = application.attributes[KompendiumAttributes.openApiSpec] - val serializableReader = application.attributes[KompendiumAttributes.schemaConfigurator] - pluginConfig.locations.forEach { (k, v) -> - val location = k.getLocationFromClass() - val path = spec.paths[location] ?: Path() - path.parameters = path.parameters?.plus(v.parameters) ?: v.parameters - v.get?.addToSpec(path, spec, v, serializableReader, location) - v.delete?.addToSpec(path, spec, v, serializableReader, location) - v.head?.addToSpec(path, spec, v, serializableReader, location) - v.options?.addToSpec(path, spec, v, serializableReader, location) - v.post?.addToSpec(path, spec, v, serializableReader, location) - v.put?.addToSpec(path, spec, v, serializableReader, location) - v.patch?.addToSpec(path, spec, v, serializableReader, location) - - spec.paths[location] = path - } - } - - @OptIn(KtorExperimentalLocationsAPI::class) - private fun KClass<*>.getLocationFromClass(): String { - // todo if parent - - val location = findAnnotation() - ?: error("Cannot notarize a location without annotating with @Location") - - val path = location.path - val parent = memberProperties.map { it.returnType.classifier as KClass<*> }.find { it.hasAnnotation() } - - return if (parent == null) { - path - } else { - parent.getLocationFromClass() + path - } - } -} diff --git a/locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt b/locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt deleted file mode 100644 index e8ec0e2d5..000000000 --- a/locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt +++ /dev/null @@ -1,119 +0,0 @@ -package io.bkbn.kompendium.locations - -import Listing -import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers -import io.bkbn.kompendium.core.fixtures.TestResponse -import io.bkbn.kompendium.core.metadata.GetInfo -import io.bkbn.kompendium.json.schema.definition.TypeDefinition -import io.bkbn.kompendium.oas.payload.Parameter -import io.kotest.core.spec.style.DescribeSpec -import io.ktor.http.HttpStatusCode -import io.ktor.server.application.call -import io.ktor.server.application.install -import io.ktor.server.locations.Locations -import io.ktor.server.locations.get -import io.ktor.server.response.respondText - -class KompendiumLocationsTest : DescribeSpec({ - describe("Location Tests") { - it("Can notarize a simple location") { - openApiTestAllSerializers( - snapshotName = "T0001__simple_location.json", - applicationSetup = { - install(Locations) - install(NotarizedLocations()) { - locations = mapOf( - Listing::class to NotarizedLocations.LocationMetadata( - parameters = listOf( - Parameter( - name = "name", - `in` = Parameter.Location.path, - schema = TypeDefinition.STRING - ), - Parameter( - name = "page", - `in` = Parameter.Location.path, - schema = TypeDefinition.INT - ) - ), - get = GetInfo.builder { - summary("Location") - description("example location") - response { - responseCode(HttpStatusCode.OK) - responseType() - description("does great things") - } - } - ), - ) - } - } - ) { - get { listing -> - call.respondText("Listing ${listing.name}, page ${listing.page}") - } - } - } - it("Can notarize nested locations") { - openApiTestAllSerializers( - snapshotName = "T0002__nested_locations.json", - applicationSetup = { - install(Locations) - install(NotarizedLocations()) { - locations = mapOf( - Type.Edit::class to NotarizedLocations.LocationMetadata( - parameters = listOf( - Parameter( - name = "name", - `in` = Parameter.Location.path, - schema = TypeDefinition.STRING - ) - ), - get = GetInfo.builder { - summary("Edit") - description("example location") - response { - responseCode(HttpStatusCode.OK) - responseType() - description("does great things") - } - } - ), - Type.Other::class to NotarizedLocations.LocationMetadata( - parameters = listOf( - Parameter( - name = "name", - `in` = Parameter.Location.path, - schema = TypeDefinition.STRING - ), - Parameter( - name = "page", - `in` = Parameter.Location.path, - schema = TypeDefinition.INT - ) - ), - get = GetInfo.builder { - summary("Other") - description("example location") - response { - responseCode(HttpStatusCode.OK) - responseType() - description("does great things") - } - } - ), - ) - } - } - ) { - get { edit -> - call.respondText("Listing ${edit.parent.name}") - } - get { other -> - call.respondText("Listing ${other.parent.name}, page ${other.page}") - } - } - } - } -}) diff --git a/locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt b/locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt deleted file mode 100644 index f120945a6..000000000 --- a/locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt +++ /dev/null @@ -1,13 +0,0 @@ -import io.ktor.server.locations.Location - -@Location("/list/{name}/page/{page}") -data class Listing(val name: String, val page: Int) - -@Location("/type/{name}") -data class Type(val name: String) { - @Location("/edit") - data class Edit(val parent: Type) - - @Location("/other/{page}") - data class Other(val parent: Type, val page: Int) -} diff --git a/locations/src/test/resources/T0001__simple_location.json b/locations/src/test/resources/T0001__simple_location.json deleted file mode 100644 index fbd638a61..000000000 --- a/locations/src/test/resources/T0001__simple_location.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "openapi": "3.1.0", - "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema", - "info": { - "title": "Test API", - "version": "1.33.7", - "description": "An amazing, fully-ish 😉 generated API spec", - "termsOfService": "https://example.com", - "contact": { - "name": "Homer Simpson", - "url": "https://gph.is/1NPUDiM", - "email": "chunkylover53@aol.com" - }, - "license": { - "name": "MIT", - "url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE" - } - }, - "servers": [ - { - "url": "https://myawesomeapi.com", - "description": "Production instance of my API" - }, - { - "url": "https://staging.myawesomeapi.com", - "description": "Where the fun stuff happens" - } - ], - "paths": { - "/list/{name}/page/{page}": { - "get": { - "tags": [], - "summary": "Location", - "description": "example location", - "parameters": [], - "responses": { - "200": { - "description": "does great things", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestResponse" - } - } - } - } - }, - "deprecated": false - }, - "parameters": [ - { - "name": "name", - "in": "path", - "schema": { - "type": "string" - }, - "required": true, - "deprecated": false - }, - { - "name": "page", - "in": "path", - "schema": { - "type": "number", - "format": "int32" - }, - "required": true, - "deprecated": false - } - ] - } - }, - "webhooks": {}, - "components": { - "schemas": { - "TestResponse": { - "type": "object", - "properties": { - "c": { - "type": "string" - } - }, - "required": [ - "c" - ] - } - }, - "securitySchemes": {} - }, - "security": [], - "tags": [] -} diff --git a/locations/src/test/resources/T0002__nested_locations.json b/locations/src/test/resources/T0002__nested_locations.json deleted file mode 100644 index 68c84bfcc..000000000 --- a/locations/src/test/resources/T0002__nested_locations.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "openapi": "3.1.0", - "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema", - "info": { - "title": "Test API", - "version": "1.33.7", - "description": "An amazing, fully-ish 😉 generated API spec", - "termsOfService": "https://example.com", - "contact": { - "name": "Homer Simpson", - "url": "https://gph.is/1NPUDiM", - "email": "chunkylover53@aol.com" - }, - "license": { - "name": "MIT", - "url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE" - } - }, - "servers": [ - { - "url": "https://myawesomeapi.com", - "description": "Production instance of my API" - }, - { - "url": "https://staging.myawesomeapi.com", - "description": "Where the fun stuff happens" - } - ], - "paths": { - "/type/{name}/edit": { - "get": { - "tags": [], - "summary": "Edit", - "description": "example location", - "parameters": [], - "responses": { - "200": { - "description": "does great things", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestResponse" - } - } - } - } - }, - "deprecated": false - }, - "parameters": [ - { - "name": "name", - "in": "path", - "schema": { - "type": "string" - }, - "required": true, - "deprecated": false - } - ] - }, - "/type/{name}/other/{page}": { - "get": { - "tags": [], - "summary": "Other", - "description": "example location", - "parameters": [], - "responses": { - "200": { - "description": "does great things", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestResponse" - } - } - } - } - }, - "deprecated": false - }, - "parameters": [ - { - "name": "name", - "in": "path", - "schema": { - "type": "string" - }, - "required": true, - "deprecated": false - }, - { - "name": "page", - "in": "path", - "schema": { - "type": "number", - "format": "int32" - }, - "required": true, - "deprecated": false - } - ] - } - }, - "webhooks": {}, - "components": { - "schemas": { - "TestResponse": { - "type": "object", - "properties": { - "c": { - "type": "string" - } - }, - "required": [ - "c" - ] - } - }, - "securitySchemes": {} - }, - "security": [], - "tags": [] -} diff --git a/oas/build.gradle.kts b/oas/build.gradle.kts index 89e1c8d0e..e19d5f0b7 100644 --- a/oas/build.gradle.kts +++ b/oas/build.gradle.kts @@ -19,10 +19,11 @@ sourdoughLibrary { dependencies { // Versions val detektVersion: String by project + val kotlinSerializeVersion: String by project api(projects.kompendiumJsonSchema) api(projects.kompendiumEnrichment) - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializeVersion") // Formatting detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion") diff --git a/playground/build.gradle.kts b/playground/build.gradle.kts index 7f936cf71..0af078110 100644 --- a/playground/build.gradle.kts +++ b/playground/build.gradle.kts @@ -13,11 +13,11 @@ sourdoughApp { dependencies { // IMPLEMENTATION implementation(projects.kompendiumCore) - implementation(projects.kompendiumLocations) implementation(projects.kompendiumResources) implementation(projects.kompendiumProtobufJavaConverter) // Ktor + val kotlinSerializeVersion: String by project val ktorVersion: String by project implementation("io.ktor:ktor-server-core:$ktorVersion") @@ -30,7 +30,6 @@ dependencies { implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") implementation("io.ktor:ktor-serialization-jackson:$ktorVersion") implementation("io.ktor:ktor-serialization-gson:$ktorVersion") - implementation("io.ktor:ktor-server-locations:$ktorVersion") implementation("io.ktor:ktor-server-resources:$ktorVersion") // Logging @@ -41,9 +40,9 @@ dependencies { implementation("org.slf4j:slf4j-simple:2.0.16") // YAML - implementation("com.charleskorn.kaml:kaml:0.59.0") + implementation("com.charleskorn.kaml:kaml:0.61.0") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializeVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1") implementation("joda-time:joda-time:2.13.0") diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt index 393082fbc..7bfc1ece2 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt @@ -15,7 +15,6 @@ import io.bkbn.kompendium.playground.util.Util.baseSpec import io.ktor.http.HttpStatusCode import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application -import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.auth.Authentication import io.ktor.server.auth.UserIdPrincipal diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt index 98f357101..a23206ef1 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt @@ -15,7 +15,6 @@ import io.bkbn.kompendium.playground.util.Util.baseSpec import io.ktor.http.HttpStatusCode import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application -import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.auth.Authentication import io.ktor.server.auth.UserIdPrincipal diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt deleted file mode 100644 index 42885a9fa..000000000 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt +++ /dev/null @@ -1,82 +0,0 @@ -package io.bkbn.kompendium.playground - -import io.bkbn.kompendium.core.metadata.GetInfo -import io.bkbn.kompendium.core.plugin.NotarizedApplication -import io.bkbn.kompendium.core.routes.redoc -import io.bkbn.kompendium.core.routes.swagger -import io.bkbn.kompendium.json.schema.definition.TypeDefinition -import io.bkbn.kompendium.locations.NotarizedLocations -import io.bkbn.kompendium.oas.payload.Parameter -import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule -import io.bkbn.kompendium.playground.util.ExampleResponse -import io.bkbn.kompendium.playground.util.Listing -import io.bkbn.kompendium.playground.util.Util.baseSpec -import io.ktor.http.HttpStatusCode -import io.ktor.serialization.kotlinx.json.json -import io.ktor.server.application.Application -import io.ktor.server.application.call -import io.ktor.server.application.install -import io.ktor.server.cio.CIO -import io.ktor.server.engine.embeddedServer -import io.ktor.server.locations.Locations -import io.ktor.server.locations.get -import io.ktor.server.plugins.contentnegotiation.ContentNegotiation -import io.ktor.server.response.respondText -import io.ktor.server.routing.routing -import kotlinx.serialization.json.Json - -fun main() { - embeddedServer( - CIO, - port = 8081, - module = Application::mainModule - ).start(wait = true) -} - -private fun Application.mainModule() { - install(Locations) - install(ContentNegotiation) { - json(Json { - serializersModule = KompendiumSerializersModule.module - encodeDefaults = true - explicitNulls = false - }) - } - install(NotarizedApplication()) { - spec = { baseSpec } - } - install(NotarizedLocations()) { - locations = mapOf( - Listing::class to NotarizedLocations.LocationMetadata( - parameters = listOf( - Parameter( - name = "name", - `in` = Parameter.Location.path, - schema = TypeDefinition.STRING - ), - Parameter( - name = "page", - `in` = Parameter.Location.path, - schema = TypeDefinition.INT - ) - ), - get = GetInfo.builder { - summary("Get user by id") - description("A very neat endpoint!") - response { - responseCode(HttpStatusCode.OK) - responseType() - description("Will return whether or not the user is real 😱") - } - } - ), - ) - } - routing { - swagger(pageTitle = "Simple API Docs") - redoc(pageTitle = "Simple API Docs") - get { listing -> - call.respondText("Listing ${listing.name}, page ${listing.page}") - } - } -} diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/util/Models.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/util/Models.kt index 31727437f..cb6471d28 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/util/Models.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/util/Models.kt @@ -1,7 +1,5 @@ package io.bkbn.kompendium.playground.util -import io.ktor.server.locations.KtorExperimentalLocationsAPI -import io.ktor.server.locations.Location import kotlinx.datetime.Instant import kotlinx.serialization.Serializable @@ -29,12 +27,3 @@ data class CustomTypeResponse( @Serializable data class ExceptionResponse(val message: String) - -@Location("/list/{name}/page/{page}") -data class Listing(val name: String, val page: Int) - -@Location("/type/{name}") data class Type(val name: String) { - // In these classes we have to include the `name` property matching the parent. - @Location("/edit") data class Edit(val parent: Type) - @Location("/other/{page}") data class Other(val parent: Type, val page: Int) -} diff --git a/protobuf-java-converter/build.gradle.kts b/protobuf-java-converter/build.gradle.kts index 5be5c9c9e..ac0e2ad2d 100644 --- a/protobuf-java-converter/build.gradle.kts +++ b/protobuf-java-converter/build.gradle.kts @@ -19,12 +19,13 @@ sourdoughLibrary { dependencies { // Versions val detektVersion: String by project - + val kotlinVersion: String by project + val kotlinSerializeVersion: String by project implementation(projects.kompendiumJsonSchema) implementation("com.google.protobuf:protobuf-java:3.25.5") - implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.25") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializeVersion") // Formatting detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion") diff --git a/resources/build.gradle.kts b/resources/build.gradle.kts index cf10358aa..6f910791e 100644 --- a/resources/build.gradle.kts +++ b/resources/build.gradle.kts @@ -18,12 +18,13 @@ sourdoughLibrary { dependencies { // Versions val detektVersion: String by project + val ktorVersion: String by project // IMPLEMENTATION implementation(projects.kompendiumCore) - implementation("io.ktor:ktor-server-core:2.3.12") - implementation("io.ktor:ktor-server-resources:2.3.12") + implementation("io.ktor:ktor-server-core:$ktorVersion") + implementation("io.ktor:ktor-server-resources:$ktorVersion") // TESTING diff --git a/resources/src/test/kotlin/io/bkbn/kompendium/resources/KompendiumResourcesTest.kt b/resources/src/test/kotlin/io/bkbn/kompendium/resources/KompendiumResourcesTest.kt index 70be0d5ab..aa7018cc2 100644 --- a/resources/src/test/kotlin/io/bkbn/kompendium/resources/KompendiumResourcesTest.kt +++ b/resources/src/test/kotlin/io/bkbn/kompendium/resources/KompendiumResourcesTest.kt @@ -9,7 +9,6 @@ import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter import io.kotest.core.spec.style.DescribeSpec import io.ktor.http.HttpStatusCode -import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.resources.Resources import io.ktor.server.resources.get diff --git a/settings.gradle.kts b/settings.gradle.kts index 06a4d39d1..23b7773d3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,7 +4,6 @@ include("core") include("enrichment") include("oas") include("playground") -include("locations") include("json-schema") include("protobuf-java-converter") include("resources")