major: ktor 3.0.0 support - fixes #644 (#645)

This commit is contained in:
knmueller
2024-10-30 16:21:46 -04:00
committed by GitHub
parent fa7b1e5833
commit 21f922a296
44 changed files with 151 additions and 777 deletions

View File

@ -2,6 +2,9 @@
## Unreleased
- Updates for Ktor 3.x support
- Remove deprecated `NotarizedLocations`
### Added
### Changed

View File

@ -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"

View File

@ -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 {

View File

@ -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

View File

@ -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, "")

View File

@ -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") {

View File

@ -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,15 +204,13 @@ class KompendiumTest : DescribeSpec({
snapshotName = "T0072__custom_serialization_strategy.json",
notarizedApplicationConfigOverrides = {
specRoute = { spec, routing ->
routing {
route("/openapi.json") {
routing.route("/openapi.json") {
get {
call.response.headers.append("Content-Type", "application/json")
call.respondText { customJsonEncoder.encodeToString(spec) }
}
}
}
}
},
contentNegotiation = {
json(

View File

@ -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<TestResponse>()
@ -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<TestResponse>()

View File

@ -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 {

View File

@ -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<TransientObject>()
fun Routing.unbackedFieldsResponse() = basicGetGenerator<UnbackedObject>()
fun Routing.customFieldNameResponse() = basicGetGenerator<SerialNameObject>()
fun Route.ignoredFieldsResponse() = basicGetGenerator<TransientObject>()
fun Route.unbackedFieldsResponse() = basicGetGenerator<UnbackedObject>()
fun Route.customFieldNameResponse() = basicGetGenerator<SerialNameObject>()

View File

@ -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<TestResponse>(
fun Route.defaultParameter() = basicGetGenerator<TestResponse>(
params = listOf(
Parameter(
name = "id",

View File

@ -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 {

View File

@ -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<TestResponse>()
authenticate("basic") {

View File

@ -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<TestResponse>(
fun Route.exampleParams() = basicGetGenerator<TestResponse>(
params = listOf(
Parameter(
name = "id",
@ -57,7 +56,7 @@ fun Routing.exampleParams() = basicGetGenerator<TestResponse>(
)
)
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 {

View File

@ -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 {

View File

@ -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<TestResponse>(operationId = "getThisDude")
fun Routing.nullableNestedObject() = basicGetGenerator<ProfileUpdateRequest>()
fun Routing.nullableEnumField() = basicGetGenerator<NullableEnum>()
fun Routing.nullableReference() = basicGetGenerator<ManyThings>()
fun Routing.dateTimeString() = basicGetGenerator<DateTimeString>()
fun Routing.headerParameter() = basicGetGenerator<TestResponse>(
fun Route.withOperationId() = basicGetGenerator<TestResponse>(operationId = "getThisDude")
fun Route.nullableNestedObject() = basicGetGenerator<ProfileUpdateRequest>()
fun Route.nullableEnumField() = basicGetGenerator<NullableEnum>()
fun Route.nullableReference() = basicGetGenerator<ManyThings>()
fun Route.dateTimeString() = basicGetGenerator<DateTimeString>()
fun Route.headerParameter() = basicGetGenerator<TestResponse>(
params = listOf(
Parameter(
name = "X-User-Email",
@ -42,10 +41,10 @@ fun Routing.headerParameter() = basicGetGenerator<TestResponse>(
)
)
fun Routing.nestedTypeName() = basicGetGenerator<Nested.Response>()
fun Routing.topLevelNullable() = basicGetGenerator<TestResponse?>()
fun Routing.simpleRecursive() = basicGetGenerator<ColumnSchema>()
fun Routing.samePathDifferentMethodsAndAuth() {
fun Route.nestedTypeName() = basicGetGenerator<Nested.Response>()
fun Route.topLevelNullable() = basicGetGenerator<TestResponse?>()
fun Route.simpleRecursive() = basicGetGenerator<ColumnSchema>()
fun Route.samePathDifferentMethodsAndAuth() {
route(defaultPath) {
install(NotarizedRoute()) {
parameters = defaultParams

View File

@ -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 {

View File

@ -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<FlibbityGibbit>()
fun Routing.polymorphicCollectionResponse() = basicGetGenerator<List<FlibbityGibbit>>()
fun Routing.polymorphicMapResponse() = basicGetGenerator<Map<String, FlibbityGibbit>>()
fun Routing.simpleGenericResponse() = basicGetGenerator<Gibbity<String>>()
fun Routing.gnarlyGenericResponse() = basicGetGenerator<Foosy<Barzo<Int>, String>>()
fun Routing.nestedGenericResponse() = basicGetGenerator<Gibbity<Map<String, String>>>()
fun Routing.genericPolymorphicResponse() = basicGetGenerator<Flibbity<Double>>()
fun Routing.genericPolymorphicResponseMultipleImpls() = basicGetGenerator<Flibbity<FlibbityGibbit>>()
fun Routing.nestedGenericCollection() = basicGetGenerator<Page<Int>>()
fun Routing.nestedGenericMultipleParamsCollection() = basicGetGenerator<MultiNestedGenerics<String, ComplexRequest>>()
fun Routing.overrideSealedTypeIdentifier() = basicGetGenerator<ChillaxificationMaximization>()
fun Routing.subtypeNotCompleteSetOfParentProperties() = basicGetGenerator<Gizmo>()
fun Route.polymorphicResponse() = basicGetGenerator<FlibbityGibbit>()
fun Route.polymorphicCollectionResponse() = basicGetGenerator<List<FlibbityGibbit>>()
fun Route.polymorphicMapResponse() = basicGetGenerator<Map<String, FlibbityGibbit>>()
fun Route.simpleGenericResponse() = basicGetGenerator<Gibbity<String>>()
fun Route.gnarlyGenericResponse() = basicGetGenerator<Foosy<Barzo<Int>, String>>()
fun Route.nestedGenericResponse() = basicGetGenerator<Gibbity<Map<String, String>>>()
fun Route.genericPolymorphicResponse() = basicGetGenerator<Flibbity<Double>>()
fun Route.genericPolymorphicResponseMultipleImpls() = basicGetGenerator<Flibbity<FlibbityGibbit>>()
fun Route.nestedGenericCollection() = basicGetGenerator<Page<Int>>()
fun Route.nestedGenericMultipleParamsCollection() = basicGetGenerator<MultiNestedGenerics<String, ComplexRequest>>()
fun Route.overrideSealedTypeIdentifier() = basicGetGenerator<ChillaxificationMaximization>()
fun Route.subtypeNotCompleteSetOfParentProperties() = basicGetGenerator<Gizmo>()

View File

@ -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<TestResponse>(
fun Route.requiredParams() = basicGetGenerator<TestResponse>(
params = listOf(
Parameter(
name = "id",
@ -17,7 +17,7 @@ fun Routing.requiredParams() = basicGetGenerator<TestResponse>(
)
)
fun Routing.nonRequiredParam() = basicGetGenerator<TestResponse>(
fun Route.nonRequiredParam() = basicGetGenerator<TestResponse>(
params = listOf(
Parameter(
name = "id",
@ -28,5 +28,5 @@ fun Routing.nonRequiredParam() = basicGetGenerator<TestResponse>(
)
)
fun Routing.defaultField() = basicGetGenerator<DefaultField>()
fun Routing.nullableField() = basicGetGenerator<NullableField>()
fun Route.defaultField() = basicGetGenerator<DefaultField>()
fun Route.nullableField() = basicGetGenerator<NullableField>()

View File

@ -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") {

View File

@ -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

View File

@ -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<KType, JsonSchema> = 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<KType, JsonSchema> = 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)
}
}

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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<ExampleResponse>()
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!

View File

@ -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

View File

@ -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")

View File

@ -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()
}
}
}

View File

@ -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<String> = emptySet(),
override var parameters: List<Parameter> = emptyList(),
override var get: GetInfo? = null,
override var post: PostInfo? = null,
override var put: PutInfo? = null,
override var delete: DeleteInfo? = null,
override var patch: PatchInfo? = null,
override var head: HeadInfo? = null,
override var options: OptionsInfo? = null,
override var security: Map<String, List<String>>? = null,
) : SpecConfig
class Config {
lateinit var locations: Map<KClass<*>, LocationMetadata>
}
operator fun invoke() = createApplicationPlugin(
name = "NotarizedLocations",
createConfiguration = ::Config
) {
val spec = application.attributes[KompendiumAttributes.openApiSpec]
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<Location>()
?: error("Cannot notarize a location without annotating with @Location")
val path = location.path
val parent = memberProperties.map { it.returnType.classifier as KClass<*> }.find { it.hasAnnotation<Location>() }
return if (parent == null) {
path
} else {
parent.getLocationFromClass() + path
}
}
}

View File

@ -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<TestResponse>()
description("does great things")
}
}
),
)
}
}
) {
get<Listing> { listing ->
call.respondText("Listing ${listing.name}, page ${listing.page}")
}
}
}
it("Can notarize nested locations") {
openApiTestAllSerializers(
snapshotName = "T0002__nested_locations.json",
applicationSetup = {
install(Locations)
install(NotarizedLocations()) {
locations = mapOf(
Type.Edit::class to NotarizedLocations.LocationMetadata(
parameters = listOf(
Parameter(
name = "name",
`in` = Parameter.Location.path,
schema = TypeDefinition.STRING
)
),
get = GetInfo.builder {
summary("Edit")
description("example location")
response {
responseCode(HttpStatusCode.OK)
responseType<TestResponse>()
description("does great things")
}
}
),
Type.Other::class to NotarizedLocations.LocationMetadata(
parameters = listOf(
Parameter(
name = "name",
`in` = Parameter.Location.path,
schema = TypeDefinition.STRING
),
Parameter(
name = "page",
`in` = Parameter.Location.path,
schema = TypeDefinition.INT
)
),
get = GetInfo.builder {
summary("Other")
description("example location")
response {
responseCode(HttpStatusCode.OK)
responseType<TestResponse>()
description("does great things")
}
}
),
)
}
}
) {
get<Type.Edit> { edit ->
call.respondText("Listing ${edit.parent.name}")
}
get<Type.Other> { other ->
call.respondText("Listing ${other.parent.name}, page ${other.page}")
}
}
}
}
})

View File

@ -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)
}

View File

@ -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": []
}

View File

@ -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": []
}

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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<ExampleResponse>()
description("Will return whether or not the user is real 😱")
}
}
),
)
}
routing {
swagger(pageTitle = "Simple API Docs")
redoc(pageTitle = "Simple API Docs")
get<Listing> { listing ->
call.respondText("Listing ${listing.name}, page ${listing.page}")
}
}
}

View File

@ -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)
}

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -4,7 +4,6 @@ include("core")
include("enrichment")
include("oas")
include("playground")
include("locations")
include("json-schema")
include("protobuf-java-converter")
include("resources")