All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.papsign.ktor.openapigen.OpenAPIGen.kt Maven / Gradle / Ivy

package com.papsign.ktor.openapigen

import com.papsign.ktor.openapigen.model.base.OpenAPIModel
import com.papsign.ktor.openapigen.model.info.ContactModel
import com.papsign.ktor.openapigen.model.info.ExternalDocumentationModel
import com.papsign.ktor.openapigen.model.info.InfoModel
import com.papsign.ktor.openapigen.model.server.ServerModel
import com.papsign.ktor.openapigen.modules.CachingModuleProvider
import com.papsign.ktor.openapigen.modules.OpenAPIModule
import io.ktor.server.application.ApplicationCallPipeline
import io.ktor.server.application.BaseApplicationPlugin
import io.ktor.server.application.call
import io.ktor.server.request.path
import io.ktor.server.response.respond
import io.ktor.server.response.respondRedirect
import io.ktor.util.AttributeKey
import org.reflections.Reflections
import kotlin.reflect.full.starProjectedType

class OpenAPIGen(
    config: Configuration,
    @Deprecated("Will be replaced with less dangerous alternative when the use case has been fleshed out.") val pipeline: ApplicationCallPipeline
) {
    private val log = classLogger()

    val api = config.api

    private val tags = HashMap()

    val globalModuleProvider = CachingModuleProvider()

    init {
        (config.scanPackagesForModules + javaClass.`package`.name).forEach { packageName ->
            val reflections = Reflections(packageName)
            log.debug("Registering modules in package $packageName")
            val objects = reflections.getSubTypesOf(OpenAPIGenExtension::class.java).mapNotNull { it.kotlin.objectInstance }
            objects.forEach {
                log.trace("Registering global module: ${it::class.simpleName}")
                it.onInit(this)
            }
        }
        config.removeModules.forEach(globalModuleProvider::unRegisterModule)
        config.addModules.forEach { globalModuleProvider.registerModule(it, it::class.starProjectedType) }
    }

    class Configuration(val api: OpenAPIModel) {
        inline fun info(crossinline configure: InfoModel.() -> Unit) {
            api.info = InfoModel().apply(configure)
        }

        inline fun InfoModel.contact(crossinline configure: ContactModel.() -> Unit) {
            contact = ContactModel().apply(configure)
        }

        inline fun server(url: String, crossinline configure: ServerModel.() -> Unit = {}) {
            api.servers.add(ServerModel(url).apply(configure))
        }

        inline fun externalDocs(url: String, crossinline configure: ExternalDocumentationModel.() -> Unit = {}) {
            api.externalDocs = ExternalDocumentationModel(url).apply(configure)
        }

        var openApiJsonPath = "/openapi.json"
        var serveOpenApiJson = true

        var swaggerUiPath = "swagger-ui"
        var serveSwaggerUi = true
        var swaggerUiVersion = "5.17.2"

        var scanPackagesForModules: Array = arrayOf()

        var addModules = mutableListOf()
        var removeModules = mutableListOf()

        fun addModules(vararg modules: OpenAPIModule) {
            addModules.addAll(modules)
        }

        fun addModules(modules: Iterable) {
            addModules.addAll(modules)
        }

        fun removeModules(vararg modules: OpenAPIModule) {
            removeModules.addAll(modules)
        }

        fun removeModules(modules: Iterable) {
            removeModules.addAll(modules)
        }

        fun replaceModule(delete: OpenAPIModule, add: OpenAPIModule) {
            addModules.add(add)
            removeModules.add(delete)
        }
    }


    fun getOrRegisterTag(tag: APITag): String {
        val other = tags.getOrPut(tag.name) {
            api.tags.add(tag.toTag())
            tag
        }
        if (other != tag) error("TagModule named ${tag.name} is already defined")
        return tag.name
    }

    companion object Plugin : BaseApplicationPlugin {
        override val key = AttributeKey("OpenAPI Generator")

        override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): OpenAPIGen {
            val api = OpenAPIModel()
            val cfg = Configuration(api).apply(configure)

            if (cfg.serveOpenApiJson) {
                pipeline.intercept(ApplicationCallPipeline.Call) {
                    if (call.request.path() == cfg.openApiJsonPath) {
                        call.respond(api.serialize())
                    }
                }
            }

            if (cfg.serveSwaggerUi) {
                val ui = SwaggerUi(cfg.swaggerUiPath, cfg.swaggerUiVersion, if (cfg.serveOpenApiJson) cfg.openApiJsonPath else null)
                val swaggerRoot = "/${cfg.swaggerUiPath.removePrefix("/")}"
                val swaggerUiResources = "/${cfg.swaggerUiPath.trim('/')}/"
                pipeline.intercept(ApplicationCallPipeline.Call) {
                    when {
                        call.request.path() == swaggerRoot -> call.respondRedirect("${swaggerRoot}/index.html")
                        call.request.path().startsWith(swaggerUiResources) ->
                            ui.serve(call.request.path().removePrefix(swaggerUiResources), call)
                    }
                }
            }
            return OpenAPIGen(cfg, pipeline)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy