Describe the bug
When generating endpoints from OpenAPI specification using EndpointGen, optional OpenAPI endpoint query params do not become optional QueryCodec[A].
This behavior alters the expected endpoint paths and may lead to discrepancies between the OpenAPI specification and the generated code.
To Reproduce
//> using scala 3.3.8
//> using dep dev.zio::zio-http-gen:3.11.3
import zio.http.endpoint.openapi.OpenAPI
import zio.http.gen.openapi.{Config, EndpointGen}
import zio.http.gen.scala.CodeGen
object OptionalQueryParamGen extends App {
val spec: String =
"""|{
| "openapi": "3.0.3",
| "info": { "title": "Users API", "version": "1.0.0" },
| "paths": {
| "/users": {
| "get": {
| "parameters": [
| {
| "name": "limit",
| "in": "query",
| "required": false,
| "schema": { "type": "integer", "format": "int32" }
| },
| {
| "name": "name",
| "in": "query",
| "required": false,
| "schema": { "type": "string" }
| }
| ],
| "responses": {
| "200": {
| "description": "OK",
| "content": {
| "application/json": {
| "schema": {
| "type": "array",
| "items": {
| "$ref": "#/components/schemas/User"
| }
| }
| }
| }
| }
| }
| }
| }
| },
| "components": {
| "schemas": {
| "User": {
| "type": "object",
| "properties": {
| "id": { "type": "integer" },
| "name": { "type": "string" }
| }
| }
| }
| }
|}
|""".stripMargin
val openApi: OpenAPI =
OpenAPI.fromJson(spec).fold(err => sys.error(s"Failed to parse OpenAPI spec: $err"), identity)
val rendered: Map[String, String] =
CodeGen.renderedFiles(
EndpointGen.fromOpenAPI(openApi, Config.default),
basePackage = "example",
)
rendered.foreach { case (path, content) =>
println(s"=== generated: $path ===")
println(content)
println()
}
}
Actual behaviour
val get = Endpoint(Method.GET / "users")
.query(HttpCodec.query[Int]("limit"))
.query(HttpCodec.query[String]("name"))
.in[Unit]
.out[Chunk[User]](status = Status.Ok)
Expected behaviour
val get = Endpoint(Method.GET / "users")
.query(HttpCodec.query[Int]("limit").optional)
.query(HttpCodec.query[String]("name").optional)
.in[Unit]
.out[Chunk[User]](status = Status.Ok)
so the endpoint query input is Option[Int] and Option[String] rather than Int and String which is currently the case.
Root cause
- in
zio.http.gen.openapi.EndpointGen, the query-parameter collection params.collect { case p if p.in == "query" => ... } builds Code.QueryParamCode(name, queryType) from the schema only; p.required is never consulted
Code.QueryParamCode has no optionality flag, and zio.http.gen.scala.CodeGen.renderQueryCode has a single rendering path that always emits .query(HttpCodec.query[$tpe]("$name")) — there is no code path that could emit .optional
Describe the bug
When generating endpoints from OpenAPI specification using
EndpointGen, optional OpenAPI endpoint query params do not become optionalQueryCodec[A].This behavior alters the expected endpoint paths and may lead to discrepancies between the OpenAPI specification and the generated code.
To Reproduce
Actual behaviour
Expected behaviour
so the endpoint query input is
Option[Int]andOption[String]rather thanIntandStringwhich is currently the case.Root cause
zio.http.gen.openapi.EndpointGen, the query-parameter collectionparams.collect { case p if p.in == "query" => ... }buildsCode.QueryParamCode(name, queryType)from the schema only;p.requiredis never consultedCode.QueryParamCodehas no optionality flag, andzio.http.gen.scala.CodeGen.renderQueryCodehas a single rendering path that always emits.query(HttpCodec.query[$tpe]("$name"))— there is no code path that could emit.optional