Skip to content

Conversation

@tkroman
Copy link

@tkroman tkroman commented Oct 24, 2025

for highly-newtyped/refined-typed endpoints Endpoint.show is sort of a nightmare to wade thru in the logs so just wanted to suggest this.

@adamw
Copy link
Member

adamw commented Oct 27, 2025

Thanks for the PR :)

Could you maybe share an example, of how the current .show is too verbose? Your solution might be the right one, but maybe we could fix something in .show itself, if the information there isn't useful. Or maybe we could provide an alternate show logic via an attribute, so that it works across the board, not just for client interpreters (server interpreters are probably impacted as well).

@tkroman
Copy link
Author

tkroman commented Oct 27, 2025

valid point about other interpreters.

here's an example:
image

@adamw
Copy link
Member

adamw commented Oct 28, 2025

Yeah that's rather long ;) I don't think show itself should ever produce such verbose information. That's why it's distinct from .toString in the first place. Could you maybe provide an endpoint description which would demonstrate the problematic output? Not that long of course, just anything that's overly verbose. I'll try to debug then

@tkroman
Copy link
Author

tkroman commented Oct 28, 2025

Yep, will do, thanks for taking a look.

@tkroman
Copy link
Author

tkroman commented Oct 29, 2025

Here's a somewhat compact reproduction:

object test {
  import eu.timepit.refined.api._
  import eu.timepit.refined.string._
  import eu.timepit.refined._
  import io.estatico.newtype.macros._
  import sttp.tapir._
  import sttp.tapir.codec.newtype._
  import sttp.tapir.codec.refined._
  import sttp.tapir.generic.auto._

  type NumRegex = MatchesRegex[W.`"""\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d"""`.T]
  @newtype case class NumString(value: String Refined NumRegex)
  case class Numbers(
    a: NumString,
    b: NumString,
    c: NumString,
    d: NumString,
    e: NumString,
  )

  object Numbers {
    implicit lazy val schema: Schema[Numbers] = Schema.derived
  }
  val instance = endpoint.post
    .name("test numbers endpoint")
    .tag("test")
    .in("numbers")
    .in(sttp.tapir.formBody[Numbers])
  def main(args: Array[String]): Unit = {
    println(instance.show)
  }
}
[test numbers endpoint] POST /numbers {body as application/x-www-form-urlencoded (UTF-8)}(a->(~\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d),b->(~\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d),c->(~\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d),d->(~\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d),e->(~\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d)) -> -/-

@tkroman
Copy link
Author

tkroman commented Oct 29, 2025

to narrow it down a bit more:

  import eu.timepit.refined.api._
  import eu.timepit.refined.string._
  import eu.timepit.refined._
  import io.estatico.newtype.macros._
  import sttp.tapir._
  import sttp.tapir.codec.newtype._
  import sttp.tapir.codec.refined._
  import sttp.tapir.generic.auto._

  type NumRegex = MatchesRegex[W.`"""\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d"""`.T]
  @newtype case class NumString(value: String Refined NumRegex)
  case class WrappedNumber(n: NumString)
  object WrappedNumber {
    implicit lazy val schema: Schema[WrappedNumber] = Schema.derived
  }
  def main(args: Array[String]): Unit = {
    println(WrappedNumber.schema.schemaType.asInstanceOf[SchemaType.SProduct[_]].fields.head.schema.validator.show)
  }
Some(~\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d)

it comes from the validator of the refined type:

  type NumRegex = MatchesRegex[W.`"""\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d"""`.T]
  @newtype case class NumString(value: String Refined NumRegex)
  def main(args: Array[String]): Unit = {
    println(implicitly[Schema[NumString]].validator.show)
  }

but when we .show just that schema directly:

type NumRegex = MatchesRegex[W.`"""\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d"""`.T]
  @newtype case class NumString(value: String Refined NumRegex)
  case class WrappedNumber(n: NumString)
  object WrappedNumber {
    implicit lazy val schema: Schema[WrappedNumber] = Schema.derived
  }
  def main(args: Array[String]): Unit = {
    println(implicitly[Schema[NumString]].show)
  }
schema is SString()

@adamw
Copy link
Member

adamw commented Oct 30, 2025

I'd propose a different fix: #4910
This doesn't include validator inforatmion in .show, but introduces a new more detailed variant, which is consistenly used in .showDetail. Hopefully this makes sense :)

@adamw adamw closed this Oct 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants