SMILEY4 / ktor-swagger-ui

Kotlin Ktor plugin to generate OpenAPI and provide Swagger UI
Apache License 2.0
180 stars 33 forks source link

Running from src/test/kotlin fails: "No api-spec with name 'api' registered" #143

Open waltkb opened 1 month ago

waltkb commented 1 month ago

I tried running the "Basic" example, taken from https://github.com/SMILEY4/ktor-swagger-ui/blob/develop/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt, only switching to CIO and adding the StatusPages plugin (and some logging configuration):

fun main() {
    embeddedServer(CIO, port = 8080, host = "localhost", module = Application::myModule).start(wait = true)
}

private fun Application.myModule() {

    install(StatusPages) {
        exception<Exception> { call, exception ->
            call.respond(HttpStatusCode.BadRequest, exception.message ?: exception.localizedMessage)
            exception.printStackTrace()
        }
    }

    // Install and configure the "SwaggerUI"-Plugin
    install(SwaggerUI) {
        // configure basic information about the api
        info {
            title = "Example API"
            description = "An example api to showcase basic swagger-ui functionality."
        }
        // provide a reference to an external documentation
        externalDocs {
            url = "https://github.com/SMILEY4/ktor-swagger-ui/wiki"
            description = "Sample external documentation"
        }
        // configure the servers from where the api is being served
        server {
            url = "http://localhost:8080"
            description = "Development Server"
        }
        server {
            url = "https://www.example.com"
            description = "Production Server"
        }
    }

    routing {

        // Create a route for the swagger-ui using the openapi-spec at "/api.json".
        // This route will not be included in the spec.
        route("swagger") {
            swaggerUI("/api.json")
        }
        // Create a route for the openapi-spec file.
        // This route will not be included in the spec.
        route("api.json") {
            openApiSpec()
        }

        // a documented route
        get("hello", {
            // description of the route
            description = "A Hello-World route"
            // information about the request
            request {
                // information about the query-parameter "name" of type "string"
                queryParameter<String>("name") {
                    description = "the name to greet"
                }
            }
            // information about possible responses
            response {
                // information about a "200 OK" response
                code(HttpStatusCode.OK) {
                    // a description of the response
                    description = "successful request - always returns 'Hello World!'"
                }
            }
        }) {
            call.respondText("Hello ${call.request.queryParameters["name"]}")
        }
    }
}

With this example, accessing the URLs does not work for me: screenshot_20241016_134426 screenshot_20241016_134436

The following exception trace is printed when accessing /api.json

INFO [DefaultDispatcher-worker-1] ktor.application : Application started in 0.341 seconds.
INFO [DefaultDispatcher-worker-1] ktor.application : Responding at http://localhost:8080
TRACE [DefaultDispatcher-worker-9] io.ktor.server.plugins.statuspages.StatusPages : Call /api.json failed with cause java.util.NoSuchElementException: No api-spec with name 'api' registered.
TRACE [DefaultDispatcher-worker-9] io.ktor.server.plugins.statuspages.StatusPages : Executing (io.ktor.server.application.ApplicationCall, kotlin.Exception /* = java.lang.Exception */) -> kotlin.Unit for exception java.util.NoSuchElementException: No api-spec with name 'api' registered. for call /api.json
java.util.NoSuchElementException: No api-spec with name 'api' registered.
    at io.github.smiley4.ktorswaggerui.routing.ApiSpec.get(ApiSpec.kt:21)
    at io.github.smiley4.ktorswaggerui.routing.RoutingKt$openApiSpec$2$1$1.invokeSuspend(routing.kt:22)
    at io.github.smiley4.ktorswaggerui.routing.RoutingKt$openApiSpec$2$1$1.invoke(routing.kt)
    at io.github.smiley4.ktorswaggerui.routing.RoutingKt$openApiSpec$2$1$1.invoke(routing.kt)
    at io.ktor.server.response.ApplicationResponseFunctionsKt.respondText(ApplicationResponseFunctions.kt:139)
    at io.github.smiley4.ktorswaggerui.routing.RoutingKt$openApiSpec$2$1.invokeSuspend(routing.kt:22)
    at io.github.smiley4.ktorswaggerui.routing.RoutingKt$openApiSpec$2$1.invoke(routing.kt)
    at io.github.smiley4.ktorswaggerui.routing.RoutingKt$openApiSpec$2$1.invoke(routing.kt)
    <51 internal lines>
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    <7 internal lines>

I am using the latest version of the dependency: implementation("io.github.smiley4:ktor-swagger-ui:3.5.1")

waltkb commented 1 month ago

I found out that this issue only occurs when running this main function from src/test/kotlin, and when moving the file to src/main/kotlin this error does not occur. Do you have any idea what could be the issue here, because all other ktor plugins etc work fine, no matter where they run from?

SMILEY4 commented 1 month ago

That sounds strange. Can't think of any reason why it would behave differently based on the file location :thinking: How are you running the example? E.g. via the IDE or with a command ... ?