hey! I seem to be getting a Swagger generation iss...
# http4k
j
hey! I seem to be getting a Swagger generation issue due to adding this line to my Custom Jackson
.setPropertyNamingStrategy(SNAKE_CASE)
v5.41.0 full stack trace
Copy code
{
  "event": {
    "exception": "java.lang.NullPointerException\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.toJsonKey(AutoJsonToJsonSchema.kt:200)\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.toMapSchema(AutoJsonToJsonSchema.kt:155)\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.toObjectOrMapSchema(AutoJsonToJsonSchema.kt:114)\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.makePropertySchemaFor(AutoJsonToJsonSchema.kt:191)\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.toObjectSchema(AutoJsonToJsonSchema.kt:128)\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.toObjectOrMapSchema(AutoJsonToJsonSchema.kt:115)\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.toSchema(AutoJsonToJsonSchema.kt:49)\n\tat org.http4k.contract.jsonschema.v3.AutoJsonToJsonSchema.toSchema(AutoJsonToJsonSchema.kt:33)\n\tat org.http4k.contract.openapi.ApiRenderer$Companion$Auto$fallbackSchema$1.toSchema(ApiRenderer.kt:31)\n\tat org.http4k.contract.openapi.ApiRenderer$Companion$Auto$1.toSchema(ApiRenderer.kt)\n\tat org.http4k.contract.openapi.v3.OpenApi3.toSchemaContent(OpenApi3.kt:335)\n\tat org.http4k.contract.openapi.v3.OpenApi3.requestBody(OpenApi3.kt:299)\n\tat org.http4k.contract.openapi.v3.OpenApi3.apiPath(OpenApi3.kt:143)\n\tat org.http4k.contract.openapi.v3.OpenApi3.apiPath(OpenApi3.kt:128)\n\tat org.http4k.contract.openapi.v3.OpenApi3.asPath(OpenApi3.kt:125)\n\tat org.http4k.contract.openapi.v3.OpenApi3.description(OpenApi3.kt:90)\n\tat org.http4k.contract.ContractRoutingHttpHandler.descriptionRoute$lambda$7$lambda$6(ContractRoutingHttpHandler.kt:78)\n\tat org.http4k.routing.RouterMatch$MatchingHandler.invoke(Router.kt:65)\n\tat org.http4k.routing.RouterMatch$MatchingHandler.invoke(Router.kt:60)\n\tat api.config.ServerFilters$CatchAll$invoke$1$1.invoke(ServerFilters.kt:51)\n\tat api.config.ServerFilters$CatchAll$invoke$1$1.invoke(ServerFilters.kt:49)\n\tat org.http4k.filter.ServerFilters$Cors$invoke$1.invoke$lambda$8(ServerFilters.kt:61)\n\tat org.http4k.filter.ResponseFilters$ReportHttpTransaction$invoke$4.invoke$lambda$2(ResponseFilters.kt:54)\n\tat org.http4k.filter.ServerFilters$RequestTracing$invoke$3.invoke$lambda$2$lambda$1(ServerFilters.kt:138)\n\tat org.http4k.filter.ZipkinTracesKt.ensureCurrentSpan(ZipkinTraces.kt:105)\n\tat org.http4k.filter.ServerFilters$RequestTracing$invoke$3.invoke$lambda$2(ServerFilters.kt:134)\n\tat org.http4k.contract.ContractRoutingHttpHandler.identify$lambda$14$lambda$13$lambda$12(ContractRoutingHttpHandler.kt:124)\n\tat org.http4k.routing.RouterMatch$MatchingHandler.invoke(Router.kt:65)\n\tat org.http4k.routing.RouterMatch$MatchingHandler.invoke(Router.kt:60)\n\tat org.http4k.routing.RouterBasedHttpHandler.invoke(RouterBasedHttpHandler.kt:19)\n\tat org.http4k.routing.RouterBasedHttpHandler.invoke(RouterBasedHttpHandler.kt:13)\n\tat org.http4k.filter.ServerFilters$CatchAll$invoke$2.invoke$lambda$0(ServerFilters.kt:321)\n\tat org.http4k.server.Http4kChannelHandler.channelRead0(Http4kChannelHandler.kt:40)\n\tat org.http4k.server.Http4kChannelHandler.channelRead0(Http4kChannelHandler.kt:35)\n\tat io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)\n\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:107)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)\n\tat io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)\n\tat io.netty.handler.codec.http.HttpServerKeepAliveHandler.channelRead(HttpServerKeepAliveHandler.java:64)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)\n\tat io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)\n\tat io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)\n\tat io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)\n\tat io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)\n\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)\n\tat io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)\n\tat io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)\n\tat io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)\n\tat io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)\n\tat io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)\n\tat io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)\n\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\tat java.base/java.lang.Thread.run(Thread.java:1583)\n",
    "level": "Error"
  },
  "metadata": {
    "timestamp": "2024-12-19T11:30:29.536244Z",
    "traces": {
      "trace_id": {
        "value": "655c69aa15afa49c"
      },
      "span_id": {
        "value": "a042ce8ed27d5dc1"
      },
      "parent_span_id": null,
      "sampling_decision": {
        "value": "1"
      }
    },
    "eventName": "api.config.UncaughtExceptionEvent"
  }
}
a
Not super surprising. Are you using this custom jackson just for your lenses? Or are you plugging it into the OpenApi generator too?
j
using for both. here is the OpenApi usage
Copy code
const val API_DESCRIPTION_PATH = "/swagger.json"

fun api(): RoutingHttpHandler = routes(
    swaggerUi(),
    contract {
        renderer = openApiRenderer()
        descriptionPath = API_DESCRIPTION_PATH
        routes = Appendable(
            all = ...
        )
    }
)

fun swaggerUi(): RoutingHttpHandler = routes(
    "/docs" bind swaggerUiWebjar {
        url = API_DESCRIPTION_PATH
        persistAuthorization = true
        deepLinking = true
    }
)

fun openApiRenderer(): OpenApi3<JsonNode> = OpenApi3(
    apiInfo = ApiInfo(
        title = "API",
        version = "v1.0",
        description = "API description."
    ),
    json = CustomJackson()
)
a
Right. So I find that putting a custom jackson into the
OpenApi3
renderer never works. Try putting the standard one in.
I see you're creating an
Appendable
directly. You might find it a little easier to just append to the existing one.
Copy code
contract {
  routes += "foo" bindContract ...
  routes += "bar" bindContract ...
  routes += v1Routes(service)
}
👍 1
d
I think this is due to an impedance mismatch between how the renderer looks for fields (based on the names in the JSON) and their representation in the instance of the class that it is looking in - ie. if JSON is changing the expected names compared to the actual field names then it's a recipe for trouble. What may work better is to name your fields in the classes as they are in the JSON. You might have to put up with some warnings in the IDE, but that problem may be worked around
j
thank you very much for your responses! managed to resolve it using
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
on the data class