Hey, another question regarding lenses in the cont...
# http4k
d
Hey, another question regarding lenses in the context of
OpenAPI
and
Filter
. I have something like:
Copy code
routes(
"/" bind contract {
…
}.withFilter(FilterWhichWouldLikeToUseALenseInside())
Sadly, I can’t use a lens inside of
FilterWhichWouldLikeToUseALenseInside()
?
d
You can just declare a lens outside and just pass it in?
d
Thanks for your response. I don’t get it. We spend some time on that issue at work. When I tried to create a small example it worked fine 🤷
d
when you say "can't use" - what behaviour are you seeing?
d
I received a weird message that the parameter has to be a string, which it was.
My simplified example is this one:
Copy code
val nameLens = Path.of("name")
val trackingIdLens = Header.required("tracking-id")

val app = routes(
    "/status" bind GET to { Response(OK) },
    "/" bind contract {
        renderer = OpenApi3(ApiInfo("My Application", "Version 1.0"), Jackson)
        descriptionPath = "/swagger.json"
        routes += customerEndpoint()
    }.withFilter(
        ServerFilters.CatchAll()
            .then(LogRequestName())
            .then(ServerFilters.CatchLensFailure { Response(BAD_REQUEST) })
    )
)

private fun customerEndpoint() = ("/v1/customer/name" / nameLens meta {
    summary = "..."
    description = "..."
    headers += trackingIdLens
} bindContract GET to { name: String ->
    { request: Request ->
        Response(OK).body(name)
    }
})

object LogRequestName {
    operator fun invoke(): Filter {
        return RequestFilters.Tap { request: Request ->
            println("Filter: ${request.something(nameLens)}")
        }
    }
}

private fun Request.something(nameLens: BiDiPathLens<String>) = nameLens(this)

fun main() {
    println(app(Request(GET, "/v1/customer/name/marie").header("tracking-id", "26")))
}
which works fine … I don’t get why we had an issue today on two different machines 😄
d
was it "body is not a string"? That's the standard message that comes when using JSON body lenses
d
No it was complaining that the path parameter I was looking for wasn’t a string. I used only path-lenses and header-lenses in this context.
d
Then it really doesn't make sense 🤣. Let us know if you can recreate it...
d
Yes, I’ll try my very best 🤣
Now I have the issue reproduced:
Copy code
val nameLens = Path.of("name")
val trackingIdLens = Header.required("tracking-id")

val app = routes(
    "/status" bind GET to { Response(OK) },
    "/" bind contract {
        renderer = OpenApi3(ApiInfo("My Application", "Version 1.0"), Jackson)
        descriptionPath = "/swagger.json"
        routes += customerEndpoint()
    }.withFilter(
        ServerFilters.CatchAll()
            .then(LogRequestName())
            .then(ServerFilters.CatchLensFailure { Response(BAD_REQUEST) })
    )
)

private fun customerEndpoint() = ("/v1/customer/name" / nameLens meta {
    summary = "..."
    description = "..."
    headers += trackingIdLens
} bindContract GET to { name: String ->
    { request: Request ->
        Response(OK).body(name)
    }
})

object LogRequestName {
    operator fun invoke(): Filter {
        return RequestFilters.Tap { request: Request ->
            println("Filter: ${request.something(nameLens)}")
        }
    }
}

private fun Request.something(nameLens: BiDiPathLens<String>) = nameLens(this)

fun main() {
    println(nameLens(Request(GET, "/v1/customer/name/marie")))
    println(app(Request(GET, "/v1/customer/name/marie").header("tracking-id", "6ed45217-935f-4794-94a5-30b2cfbe181b")))
}
Output:
Copy code
Exception in thread "main" org.http4k.lens.LensFailure: path 'name' must be string
	at org.http4k.lens.Lens.invoke(lens.kt:19)
	at MyApplicationKt.main(MyApplication.kt:59)
	at MyApplicationKt.main(MyApplication.kt)
Caused by: java.lang.IllegalStateException: Request was not routed, so no uri-template present
	at org.http4k.routing.ExtensionsKt.path(extensions.kt:20)
	at org.http4k.lens.PathLens$1.invoke(path.kt:18)
	at org.http4k.lens.PathLens$1.invoke(path.kt:17)
	at org.http4k.lens.Lens.invoke(lens.kt:15)
	... 2 more
Any ideas, what I’m doing wrong?
d
Ah - yes. This is due to the fact you are using both types of routing. With the contracts you cannot extract from a path because you're not using a routes block.
The path lenses only pull things out when you are using the routing dsl
(and you've got "{name}" in your path)
d
ohhhh 😕 would it work using only contract routes?
d
With contracts currently the extraction only happens when the path is parsed. It's possible we could adjust this - but without investigation I'm not sure if it would work nicely
d
okay thanks a lot for your quick response 👍