https://kotlinlang.org logo
Title
n

Nicola

04/04/2023, 10:37 AM
Hi, I've a problem trying to use OpenApi with moshi/Kotshi At this line:
renderer = OpenApi3(
    apiInfo = ApiInfo("Payment renderer service API", "0.0.1")
    json = moshiLite
    apiRenderer = OpenApi3ApiRenderer(moshiLite)
)
I get the error Cannot access class 'org.http4k.format.ConfigurableJackson'. Check your module classpath for missing or conflicting dependencies I indeed removed jackson from dependencies, trying to use moshi/kotshi. The var moshiList is as follows:
@KotshiJsonAdapterFactory
object ApplicationJsonAdapterFactory : JsonAdapter.Factory by KotshiApplicationJsonAdapterFactory

val moshiLite = ConfigurableMoshi(
    Moshi.Builder()
        .add(ApplicationJsonAdapterFactory)
        .add(ListAdapter)
        .add(MapAdapter)
        .asConfigurable()
        .withStandardMappings()
        .done()
)
where could be the problem? thanks in advance ..
d

dave

04/04/2023, 10:39 AM
yep - openAPI generation is not supported with anything other than Jackson at the moment
n

Nicola

04/04/2023, 10:43 AM
ah! i overlooked that .. but leaving it just for open-api will be a performance penalty? (in aws lambda environment I mean) ? Or will it be used when invoking the openapi endpoint only? (sorry for my english)
btw this project by Andrew https://github.com/oharaandrew314/http4k-api-on-lambda doesn't seem to include jackson, at least I don't see it but it builds correctly
d

dave

04/04/2023, 1:58 PM
Yes - @Andrew O'Hara is using some magic to do the same thing. WE hope to eventually push some of this stuff back into the library,. I wonder where it's referencing JSON then?
a

Andrew O'Hara

04/04/2023, 2:09 PM
The
ConfigurableMoshi
via Kotshi is defined here and it uses the same
AutomMarshalling
to generate the OpenAPI spec with the reflectionless
OpenApi3Renderer
here. As far as I can tell, it is completely reflectionless.
n

Nicola

04/04/2023, 2:11 PM
I tried to do the same (my code is above) but there must be something different obviously
d

dave

04/04/2023, 2:12 PM
I'd try and work out where it's referencing the Jackson.
Is there a stack trace?
a

Andrew O'Hara

04/04/2023, 2:12 PM
Kotshi doesn't have the best documentation, but here's essentially what you need to do. 1. Add the KSP plugin to your build.gradle like this (ensure you're using the right plugin for your Kotlin version) 2. Define a Kotshi adapter and reference the one built by the code generator like this
n

Nicola

04/04/2023, 2:15 PM
about 1 I am using jvm 11 and got a warning could be it the problem?
@dave sorry I have deleted the openapi part to test the rest, I will reproduce and paste the stack
a

Andrew O'Hara

04/04/2023, 2:16 PM
Ok, looking back, I can see you've already done most of what I suggested, so David's suggestion to look through the stacktrace (or follow the debugger) is going to be the best way to find out why Jackson is being used
d

dave

04/04/2023, 2:16 PM
sorry I have deleted the openapi part to test the rest, I will reproduce and paste the stack
IntelliJ local history is very good btw 🙂
n

Nicola

04/04/2023, 2:22 PM
I get a compilation error, not a stack trace actually ....
Intellij in the "Problems" view says: Cannot access class 'org.http4k.format.ConfigurableJackson'. Check your module classpath for missing or conflicting dependencies
In the tooltip IntelliJ show this: public fun OpenApi3( apiInfo: ApiInfo, json: [Error type: Unresolved type for org.http4k.format.ConfigurableJackson], extensions: List<OpenApiExtension>, servers: List<ApiServer>, version: OpenApiVersion ): OpenApi3<[Error type: Unresolved type for com.fasterxml.jackson.databind.JsonNode]
It seems that my "moshiLite" is not visible
d

dave

04/04/2023, 2:24 PM
you can try 2 things:
1. include jackson for compileOnly
(band aid)
2. try to call that function manually by passing all of the args
n

Nicola

04/04/2023, 2:25 PM
ook
d

dave

04/04/2023, 2:27 PM
Not sure why it's not working - it shouldn't even reference the Jackson really
n

Nicola

04/04/2023, 2:28 PM
maybe I have the wrong import? If I navigate to the definition of OpenApi3 in my project I land to jacksonExt.kt
while, in the Andrew''s, I land to OpenApi3
d

dave

04/04/2023, 2:29 PM
ah - you're calling the wrong overload
n

Nicola

04/04/2023, 2:29 PM
ah ... but where ? 😢
my build.gradle is this
buildscript {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}

plugins {
    id "org.jetbrains.kotlin.jvm" version "${kotlinVersion}"
    id "com.github.johnrengelman.shadow" version "7.1.2"
    id "com.google.devtools.ksp" version "1.8.10-1.0.9"
}

apply plugin: "com.github.johnrengelman.shadow"
apply plugin: "java"

repositories {
    mavenCentral()
}

compileKotlin.kotlinOptions.jvmTarget = "11"
compileTestKotlin.kotlinOptions.jvmTarget = "11"

sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

test {
    useJUnitPlatform()
    testLogging.showStandardStreams = true
}

dependencies {
    implementation platform("org.http4k:http4k-bom:${http4kVersion}")
    implementation "org.http4k:http4k-core"
    implementation "org.http4k:http4k-serverless-lambda"
    implementation "org.http4k:http4k-cloudnative"
    implementation "org.http4k:http4k-contract"
    implementation("org.http4k:http4k-format-moshi") {
        exclude group: "org.jetbrains.kotlin", module: "kotlin-reflect"
    }
    implementation("se.ansman.kotshi:api:$kotshiVersion")
    ksp("se.ansman.kotshi:compiler:$kotshiVersion")

    implementation "org.http4k:http4k-opentelemetry:${http4kVersion}"
    implementation "org.apache.pdfbox:pdfbox:2.0.27"
    implementation "de.redsix:pdfcompare:1.1.61"
    implementation "net.sf.barcode4j:barcode4j:2.1"

    testImplementation platform("org.junit:junit-bom:${junitVersion}")
    testImplementation "org.http4k:http4k-testing-hamkrest"
    testImplementation "org.junit.jupiter:junit-jupiter-api"
    testImplementation "org.junit.jupiter:junit-jupiter-engine"
}

shadowJar {
    manifest.attributes['Main-Class'] = 'it.multidialogo.multiprinter.MultiprinterKt'
    archiveBaseName.set("multiprinter")
    archiveClassifier.set(null)
    archiveVersion.set(null)
    mergeServiceFiles()
}

task buildZip(type: Zip) {
    from compileKotlin
    from processResources
    into('lib') {
        from configurations.compileClasspath
    }
}
imports where I have the problem are these:
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import it.multidialogo.multiprinter.domain.Payment
import it.multidialogo.multiprinter.domain.PaymentParty
import it.multidialogo.multiprinter.domain.PaymentType
import it.multidialogo.multiprinter.dto.MemoryMetricsDto
import it.multidialogo.multiprinter.dto.PaymentRenderRequestDto
import it.multidialogo.multiprinter.dto.PaymentRenderResponseDto
import it.multidialogo.multiprinter.dto.RenderMetricsDto
import it.multidialogo.multiprinter.services.RenderService
import it.multidialogo.multiprinter.utils.RenderMetricsDurationMillisAdapter
import org.http4k.cloudnative.env.Environment
import org.http4k.cloudnative.env.EnvironmentKey
import org.http4k.contract.contract
import org.http4k.contract.meta
import org.http4k.contract.openapi.ApiInfo
import org.http4k.contract.openapi.v3.OpenApi3
import org.http4k.contract.openapi.v3.OpenApi3ApiRenderer
import org.http4k.core.*
import org.http4k.core.Status.Companion.ACCEPTED
import org.http4k.format.*
import org.http4k.lens.Lens
import <http://org.http4k.lens.int|org.http4k.lens.int>
import org.http4k.lens.string
import org.http4k.routing.routes
import org.http4k.server.SunHttp
import org.http4k.server.asServer
import se.ansman.kotshi.KotshiJsonAdapterFactory
I don't see jackson 🤔
d

dave

04/04/2023, 2:31 PM
jackson isn't in there.
but the contract stuff ships with some bindings which will automatically pick up and use the definitions in jacksonExt.kt, The overloaded function looks like the OpenApi constructor
fun OpenApi3(....)
if you make sure to pass all of the args to the ORIGINAL OpenApi3 function, it will pick up the correct one
n

Nicola

04/04/2023, 2:35 PM
I am using the same parameters of the other project ... I will try anyway thaks a lot!
there is a (big) difference though ... one is a class, jackson version is a fun
this is the jacksonExt:
fun OpenApi3(
    apiInfo: ApiInfo,
    json: ConfigurableJackson = OpenAPIJackson,
    extensions: List<OpenApiExtension> = emptyList(),
    servers: List<ApiServer> = emptyList(),
    version: OpenApiVersion = OpenApiVersion._3_0_0
) =
    OpenApi3(apiInfo, json, extensions, ApiRenderer.Auto(json, AutoJsonToJsonSchema(json)), servers = servers, version = version)
the "working" version is this
class OpenApi3<NODE : Any>(
    private val apiInfo: ApiInfo,
    private val json: Json<NODE>,
    private val extensions: List<OpenApiExtension> = emptyList(),
    private val apiRenderer: ApiRenderer<Api<NODE>, NODE> = OpenApi3ApiRenderer(json),
    // note that this is the basic OpenApi renderer - if you want reflective Schema generation
    // then you want to use ApiRenderer.Auto() instead with a compatible JSON instance
    private val securityRenderer: SecurityRenderer = OpenApi3SecurityRenderer,
    private val errorResponseRenderer: ErrorResponseRenderer = JsonErrorResponseRenderer(json),
    private val servers: List<ApiServer> = emptyList(),
    private val version: OpenApiVersion = _3_0_0
) : ContractRenderer, ErrorResponseRenderer by errorResponseRenderer {
d

dave

04/04/2023, 2:41 PM
I wonder if it's because you're using named parameters?
a

Andrew O'Hara

04/04/2023, 2:44 PM
The arguments in their first example look the same as mine... not sure why the jackson overload is being called
n

Nicola

04/04/2023, 2:44 PM
I've tried in this form
renderer = OpenApi3<MoshiNode>(
    ApiInfo("Renderer API", "0.0.1"),
    moshiLite,
    OpenApi3ApiRenderer(moshiLite)
)
but still the jackson version is catched first
d

dave

04/04/2023, 2:46 PM
version of Java? version of Kotlin? The key will be to call a version of the function which isn't handled by the JacksonExt.kt
n

Nicola

04/04/2023, 2:46 PM
java 11 I've tried 1.8 too but with same results
I will try, there are some parameters that are in the not-jackson version only
d

dave

04/04/2023, 2:49 PM
that should definitely work. Are you working on windows/mac/linux?
a

Andrew O'Hara

04/04/2023, 2:51 PM
What version of http4k are you using? It's not defined in your gradle file.
n

Nicola

04/04/2023, 2:51 PM
forcing securityRenderer (which does not exist in the jackson version) it works:
renderer = OpenApi3(
    apiInfo = ApiInfo("Renderer API", "0.0.1"),
    json = moshiLite,
    apiRenderer = OpenApi3ApiRenderer(moshiLite),
    securityRenderer = OpenApi3SecurityRenderer
)
I am working on ubuntu 18.04
junitVersion=5.7.2
kotlinVersion=1.8.10
http4kVersion=4.41.0.0
kotshiVersion=2.10.2
a

Andrew O'Hara

04/04/2023, 2:52 PM
Bizarre. Forcing
apiRenderer
should have worked, because it's not even an option for the Jackson overload. But glad it's working for you now 🤷
n

Nicola

04/04/2023, 2:54 PM
I will re-try removing securityRenderer, but maybe tomorrow 🙂 thanks a lot again
Not sure it may be a useful information but I think the problem was that I was using http4k 4.41.0.0 updating to 4.41.1.0 solves the problem (i have the right overload without adding securityRenderer)
d

dave

04/04/2023, 3:09 PM
I've no idea what changed, but "always be updatin'!" 😂
it could have just been clearing gradle/build caches would have done it. Sometimes I get the impression that all may not be totally deterministic around this stuff.. 😉