Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No value type configured for ObjectReader #237

Open
brys0 opened this issue Dec 31, 2024 · 3 comments
Labels
investigation question Further information is requested

Comments

@brys0
Copy link

brys0 commented Dec 31, 2024

Actual behavior (the bug)
When registering OpenApiPlugin with a definition configuration (even if it is empty/blank) trying to access the openapi.json documentation causes a server error

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No value type configured for ObjectReader
 at [Source: UNKNOWN; byte offset: #UNKNOWN]
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1887)
	at com.fasterxml.jackson.databind.ObjectReader._findRootDeserializer(ObjectReader.java:2397)
	at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2125)
	at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1744)
	at io.javalin.openapi.plugin.OpenApiPlugin.applyConfigurationTo(OpenApiPlugin.kt:47)
	at io.javalin.openapi.plugin.OpenApiPlugin.access$applyConfigurationTo(OpenApiPlugin.kt:12)
	at io.javalin.openapi.plugin.OpenApiPlugin$createDocumentation$1.invoke(OpenApiPlugin.kt:37)
	at io.javalin.openapi.plugin.OpenApiPlugin$createDocumentation$1.invoke(OpenApiPlugin.kt:25)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at io.javalin.openapi.plugin.OpenApiHandler.handle(OpenApiHandler.kt:15)
	at io.javalin.router.Endpoint.handle(Endpoint.kt:52)
	at io.javalin.router.ParsedEndpoint.handle(ParsedEndpoint.kt:15)
	at io.javalin.http.servlet.DefaultTasks.HTTP$lambda$9$lambda$7$lambda$6(DefaultTasks.kt:52)
	at io.javalin.http.servlet.JavalinServlet.handleTask(JavalinServlet.kt:99)
	at io.javalin.http.servlet.JavalinServlet.handleSync(JavalinServlet.kt:64)
	at io.javalin.http.servlet.JavalinServlet.handle(JavalinServlet.kt:50)
	at io.javalin.http.servlet.JavalinServlet.service(JavalinServlet.kt:30)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
	at io.javalin.jetty.JavalinJettyServlet.service(JavalinJettyServlet.kt:52)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:529)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1580)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1553)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
	at org.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
	at org.eclipse.jetty.server.Server.handle(Server.java:563)
	at org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
	at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
	at java.base/java.lang.Thread.run(Thread.java:1583)

Expected behavior
There shouldn't be an error for adding a definition configuration

To Reproduce
build.gradle.kts

dependencies {
 val javalin = "6.4.0"
    implementation("io.javalin:javalin:$javalin")
    implementation("io.javalin.community.openapi:javalin-openapi-plugin:$javalin")
    implementation("io.javalin.community.openapi:javalin-redoc-plugin:$javalin")
    kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalin")
}

open api setting configuration

 conf.registerPlugin(OpenApiPlugin { openapi ->
                openapi
                    .withDocumentationPath("/docs/openapi.json")
                    .withDefinitionConfiguration { version, def ->  /* Just existing causes an error **/ }
            })

Additional context
Add any other context about the bug here

@dzikoysk dzikoysk added question Further information is requested investigation labels Jan 1, 2025
@dzikoysk
Copy link
Member

dzikoysk commented Jan 1, 2025

Hey, unfortunately I'm unable to reproduce this problem in our test setup:

nor the real world app that uses config processor:

Could you create an example project affected by this bug and send it here? :)

@brys0
Copy link
Author

brys0 commented Jan 2, 2025

Hey, I'll try my best to get you a minimal reproduction sometime tomorrow hopefully.

@SuppieRK
Copy link

SuppieRK commented Feb 2, 2025

Facing the same issue, root cause seems to be in OpenApiPlugin.kt, specifically docsNode.replace("info", currentInfo.readValue(jsonMapper.convertValue(info, JsonNode::class.java))) line.

My example to reproduce the problem in Javalin 6.4.0 (Java 21):

import static io.javalin.apibuilder.ApiBuilder.get;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import io.javalin.Javalin;
import io.javalin.http.HttpStatus;
import io.javalin.openapi.plugin.OpenApiPlugin;
import io.javalin.openapi.plugin.swagger.SwaggerPlugin;
import io.javalin.testtools.JavalinTest;
import org.junit.jupiter.api.Test;

class OpenApiTest {
  final Javalin javalin =
      Javalin.create(
          config -> {
            // Registering OpenAPI
            config.registerPlugin(new SwaggerPlugin());
            config.registerPlugin(
                new OpenApiPlugin(
                    pluginConfig ->
                        pluginConfig.withDefinitionConfiguration(
                            (version, definition) ->
                                definition.withInfo(info -> info.setTitle("test")))));

            config.router.apiBuilder(() -> get("/api/health", ctx -> ctx.result("OK")));
          });

  @Test
  void hasHealthcheck() {
    JavalinTest.test(
        javalin,
        (server, client) -> {
          try (final var healthcheckResponse = client.get("/api/health")) {
            assertEquals(
                HttpStatus.OK.getCode(), healthcheckResponse.code(), "Response code is not OK");
            assertEquals(
                "OK", healthcheckResponse.body().string(), "Response body is not text saying 'OK'");
          }
        });
  }

  @Test
  void hasOpenApiAndSwaggerEndpoints() {
    JavalinTest.test(
        javalin,
        (server, client) -> {
          try (final var metricsResponse = client.get("/openapi?v=default")) {
            assertEquals(
                HttpStatus.OK.getCode(), metricsResponse.code(), "Response code is not OK");

            final var responseBody = metricsResponse.body().string();

            assertNotNull(responseBody, "Response body is null");
            assertFalse(responseBody.isBlank(), "Response body is blank");
          }

          try (final var metricsResponse = client.get("/swagger")) {
            assertEquals(
                HttpStatus.OK.getCode(), metricsResponse.code(), "Response code is not OK");

            final var responseBody = metricsResponse.body().string();

            assertNotNull(responseBody, "Response body is null");
            assertFalse(responseBody.isBlank(), "Response body is blank");
          }
        });
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
investigation question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants