Skip to content

Commit

Permalink
split into openapi and swagger modules
Browse files Browse the repository at this point in the history
  • Loading branch information
SMILEY4 committed Jan 6, 2025
1 parent 3bcb672 commit b3bcebc
Show file tree
Hide file tree
Showing 127 changed files with 753 additions and 576 deletions.
124 changes: 124 additions & 0 deletions ktor-openapi/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.KotlinJvm
import com.vanniktech.maven.publish.SonatypeHost
import io.gitlab.arturbosch.detekt.Detekt

val projectGroupId: String by project
val projectVersion: String by project
group = projectGroupId
version = projectVersion

plugins {
kotlin("jvm")
id("org.owasp.dependencycheck")
id("io.gitlab.arturbosch.detekt")
id("com.vanniktech.maven.publish")
id("org.jetbrains.dokka")
}

repositories {
mavenCentral()
}

dependencies {
val versionKtor: String by project
val versionSwaggerParser: String by project
val versionSchemaKenerator: String by project
val versionKotlinLogging: String by project
val versionKotest: String by project
val versionKotlinTest: String by project
val versionMockk: String by project
val versionJackson: String by project

implementation("io.ktor:ktor-server-core-jvm:$versionKtor")
implementation("io.ktor:ktor-server-auth:$versionKtor")
implementation("io.ktor:ktor-server-resources:$versionKtor")

implementation("io.swagger.parser.v3:swagger-parser:$versionSwaggerParser")

implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$versionJackson")

implementation("io.github.smiley4:schema-kenerator-core:$versionSchemaKenerator")
implementation("io.github.smiley4:schema-kenerator-reflection:$versionSchemaKenerator")
implementation("io.github.smiley4:schema-kenerator-swagger:$versionSchemaKenerator")

implementation("io.github.oshai:kotlin-logging-jvm:$versionKotlinLogging")

testImplementation("io.ktor:ktor-server-netty-jvm:$versionKtor")
testImplementation("io.ktor:ktor-server-content-negotiation:$versionKtor")
testImplementation("io.ktor:ktor-serialization-jackson:$versionKtor")
testImplementation("io.ktor:ktor-server-auth:$versionKtor")
testImplementation("io.ktor:ktor-server-call-logging:$versionKtor")
testImplementation("io.ktor:ktor-server-test-host:$versionKtor")
testImplementation("io.kotest:kotest-runner-junit5:$versionKotest")
testImplementation("io.kotest:kotest-assertions-core:$versionKotest")
testImplementation("org.jetbrains.kotlin:kotlin-test:$versionKotlinTest")
testImplementation("io.mockk:mockk:$versionMockk")

}

kotlin {
jvmToolchain(11)
}

tasks.withType<Test>().configureEach {
useJUnitPlatform()
}

detekt {
ignoreFailures = false
buildUponDefaultConfig = true
allRules = false
config.setFrom("$projectDir/../detekt/detekt.yml")
}
tasks.withType<Detekt>().configureEach {
reports {
html.required.set(true)
md.required.set(true)
xml.required.set(false)
txt.required.set(false)
sarif.required.set(false)
}
}

mavenPublishing {
val projectGroupId: String by project
val projectVersion: String by project
val projectArtifactIdBase: String by project
val projectNameBase: String by project
val projectDescriptionBase: String by project
val projectScmUrl: String by project
val projectScmConnection: String by project
val projectLicenseName: String by project
val projectLicenseUrl: String by project
val projectDeveloperName: String by project
val projectDeveloperUrl: String by project

configure(KotlinJvm(JavadocJar.Dokka("dokkaHtml"), true))
publishToMavenCentral(SonatypeHost.S01)
signAllPublications()
coordinates(projectGroupId, projectArtifactIdBase, projectVersion)
pom {
name.set(projectNameBase)
description.set(projectDescriptionBase)
url.set(projectScmUrl)
licenses {
license {
name.set(projectLicenseName)
url.set(projectLicenseUrl)
distribution.set(projectLicenseUrl)
}
}
scm {
url.set(projectScmUrl)
connection.set(projectScmConnection)
}
developers {
developer {
id.set(projectDeveloperName)
name.set(projectDeveloperName)
url.set(projectDeveloperUrl)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package io.github.smiley4.ktoropenapi

import io.github.oshai.kotlinlogging.KotlinLogging
import io.github.smiley4.ktoropenapi.builder.example.ExampleContext
import io.github.smiley4.ktoropenapi.builder.example.ExampleContextImpl
import io.github.smiley4.ktoropenapi.builder.openapi.ComponentsBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.ContactBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.ContentBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.ExternalDocumentationBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.HeaderBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.InfoBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.LicenseBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.OAuthFlowsBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.OpenApiBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.OperationBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.OperationTagsBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.ParameterBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.PathBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.PathsBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.RequestBodyBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.ResponseBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.ResponsesBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.SecurityRequirementsBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.SecuritySchemesBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.ServerBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.TagBuilder
import io.github.smiley4.ktoropenapi.builder.openapi.TagExternalDocumentationBuilder
import io.github.smiley4.ktoropenapi.builder.route.RouteCollector
import io.github.smiley4.ktoropenapi.builder.route.RouteDocumentationMerger
import io.github.smiley4.ktoropenapi.builder.route.RouteMeta
import io.github.smiley4.ktoropenapi.builder.schema.SchemaContextImpl
import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext
import io.github.smiley4.ktoropenapi.data.OutputFormat
import io.github.smiley4.ktoropenapi.data.PluginConfigData
import io.github.smiley4.ktoropenapi.dsl.config.OpenApiPluginConfigDsl
import io.github.smiley4.ktoropenapi.routing.ApiSpec
import io.ktor.server.application.Application
import io.ktor.server.application.ApplicationStarted
import io.ktor.server.application.createApplicationPlugin
import io.ktor.server.application.hooks.MonitoringEvent
import io.ktor.server.application.plugin
import io.ktor.server.routing.RoutingRoot
import io.swagger.v3.core.util.Json31
import io.swagger.v3.core.util.Yaml31

private val logger = KotlinLogging.logger {}

val OpenApi = createApplicationPlugin(name = "OpenApi", createConfiguration = ::OpenApiPluginConfigDsl) {

val config = pluginConfig.build(PluginConfigData.DEFAULT)

on(MonitoringEvent(ApplicationStarted)) { application ->

try {
val routes = routes(application, config)
ApiSpec.setAll(buildOpenApiSpecs(config, routes))
} catch (e: Exception) {
logger.error(e) { "Error during application startup in openapi-plugin" }
}

}
}

private fun buildOpenApiSpecs(config: PluginConfigData, routes: List<RouteMeta>): Map<String, Pair<String, OutputFormat>> {
val routesBySpec = buildMap<String, MutableList<RouteMeta>> {
routes.forEach { route ->
val specName =
route.documentation.specId ?: config.specAssigner(route.path, route.documentation.tags.toList())
computeIfAbsent(specName) { mutableListOf() }.add(route)
}
}
return buildMap {
routesBySpec.forEach { (specName, routes) ->
val specConfig = config.specConfigs[specName] ?: config
this[specName] = buildOpenApiSpec(specName, specConfig, routes)
}
}
}

private fun buildOpenApiSpec(specName: String, pluginConfig: PluginConfigData, routes: List<RouteMeta>): Pair<String, OutputFormat> {
return try {
val schemaContext = SchemaContextImpl(pluginConfig.schemaConfig).also {
it.addGlobal(pluginConfig.schemaConfig)
it.add(routes)
}
val exampleContext = ExampleContextImpl(pluginConfig.exampleConfig.exampleEncoder).also {
it.addShared(pluginConfig.exampleConfig)
it.add(routes)
}
val openApi = builder(pluginConfig, schemaContext, exampleContext).build(routes)
pluginConfig.postBuild?.let { it(openApi, specName) }
when (pluginConfig.outputFormat) {
OutputFormat.JSON -> Json31.pretty(openApi) to pluginConfig.outputFormat
OutputFormat.YAML -> Yaml31.pretty(openApi) to pluginConfig.outputFormat
}
} catch (e: Exception) {
logger.error(e) { "Error during openapi-spec generation" }
return pluginConfig.outputFormat.empty to pluginConfig.outputFormat
}
}

private fun routes(application: Application, config: PluginConfigData): List<RouteMeta> {
return RouteCollector(RouteDocumentationMerger())
.collectRoutes({ application.plugin(RoutingRoot) }, config)
.map { it.copy(path = "${application.rootPath()}${it.path}") }
.toList()
}

private fun Application.rootPath(): String =
environment.config.propertyOrNull("ktor.deployment.rootPath")?.getString() ?: ""

private fun builder(
config: PluginConfigData,
schemaContext: SchemaContext,
exampleContext: ExampleContext,
): OpenApiBuilder {
return OpenApiBuilder(
config = config,
schemaContext = schemaContext,
exampleContext = exampleContext,
infoBuilder = InfoBuilder(
contactBuilder = ContactBuilder(),
licenseBuilder = LicenseBuilder()
),
externalDocumentationBuilder = ExternalDocumentationBuilder(),
serverBuilder = ServerBuilder(),
tagBuilder = TagBuilder(
tagExternalDocumentationBuilder = TagExternalDocumentationBuilder()
),
pathsBuilder = PathsBuilder(
pathBuilder = PathBuilder(
operationBuilder = OperationBuilder(
operationTagsBuilder = OperationTagsBuilder(config),
parameterBuilder = ParameterBuilder(
schemaContext = schemaContext,
exampleContext = exampleContext,
),
requestBodyBuilder = RequestBodyBuilder(
contentBuilder = ContentBuilder(
schemaContext = schemaContext,
exampleContext = exampleContext,
headerBuilder = HeaderBuilder(schemaContext)
)
),
responsesBuilder = ResponsesBuilder(
responseBuilder = ResponseBuilder(
headerBuilder = HeaderBuilder(schemaContext),
contentBuilder = ContentBuilder(
schemaContext = schemaContext,
exampleContext = exampleContext,
headerBuilder = HeaderBuilder(schemaContext)
)
),
config = config
),
securityRequirementsBuilder = SecurityRequirementsBuilder(config),
externalDocumentationBuilder = ExternalDocumentationBuilder(),
serverBuilder = ServerBuilder()
)
)
),
componentsBuilder = ComponentsBuilder(
config = config,
securitySchemesBuilder = SecuritySchemesBuilder(
oAuthFlowsBuilder = OAuthFlowsBuilder()
)
)
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.smiley4.ktorswaggerui.builder.example
package io.github.smiley4.ktoropenapi.builder.example

import io.github.smiley4.ktorswaggerui.data.ExampleDescriptor
import io.github.smiley4.ktoropenapi.data.ExampleDescriptor
import io.swagger.v3.oas.models.examples.Example

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.smiley4.ktorswaggerui.builder.example
package io.github.smiley4.ktoropenapi.builder.example

import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta
import io.github.smiley4.ktorswaggerui.data.*
import io.github.smiley4.ktoropenapi.builder.route.RouteMeta
import io.github.smiley4.ktoropenapi.data.*
import io.swagger.v3.oas.models.examples.Example

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.data.PluginConfigData
import io.github.smiley4.ktoropenapi.data.PluginConfigData
import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.examples.Example
import io.swagger.v3.oas.models.media.Schema
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.data.ContactData
import io.github.smiley4.ktoropenapi.data.ContactData
import io.swagger.v3.oas.models.info.Contact

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext
import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext
import io.github.smiley4.ktorswaggerui.data.OpenApiBaseBodyData
import io.github.smiley4.ktorswaggerui.data.OpenApiMultipartBodyData
import io.github.smiley4.ktorswaggerui.data.OpenApiSimpleBodyData
import io.github.smiley4.ktoropenapi.builder.example.ExampleContext
import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext
import io.github.smiley4.ktoropenapi.data.OpenApiBaseBodyData
import io.github.smiley4.ktoropenapi.data.OpenApiMultipartBodyData
import io.github.smiley4.ktoropenapi.data.OpenApiSimpleBodyData
import io.ktor.http.*
import io.swagger.v3.oas.models.media.Content
import io.swagger.v3.oas.models.media.Encoding
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.data.ExternalDocsData
import io.github.smiley4.ktoropenapi.data.ExternalDocsData
import io.swagger.v3.oas.models.ExternalDocumentation

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext
import io.github.smiley4.ktorswaggerui.data.OpenApiHeaderData
import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext
import io.github.smiley4.ktoropenapi.data.OpenApiHeaderData
import io.swagger.v3.oas.models.ExternalDocumentation
import io.swagger.v3.oas.models.headers.Header

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.data.InfoData
import io.github.smiley4.ktoropenapi.data.InfoData
import io.swagger.v3.oas.models.info.Info

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.data.LicenseData
import io.github.smiley4.ktoropenapi.data.LicenseData
import io.swagger.v3.oas.models.info.License

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.smiley4.ktorswaggerui.builder.openapi
package io.github.smiley4.ktoropenapi.builder.openapi

import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData
import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowsData
import io.github.smiley4.ktoropenapi.data.OpenIdOAuthFlowData
import io.github.smiley4.ktoropenapi.data.OpenIdOAuthFlowsData
import io.swagger.v3.oas.models.security.OAuthFlow
import io.swagger.v3.oas.models.security.OAuthFlows
import io.swagger.v3.oas.models.security.Scopes
Expand Down
Loading

0 comments on commit b3bcebc

Please sign in to comment.