softwaremill / tapir

Rapid development of self-documenting APIs
https://tapir.softwaremill.com
Apache License 2.0
1.33k stars 404 forks source link

support Validator preservation #3408

Open eanea opened 7 months ago

eanea commented 7 months ago

Tapir version: 1.9.5 Scala version: 2.13.12

Describe the bug Can't find a way to keep the validator in case of an optional field

How to reproduce? https://scastie.scala-lang.org/GVBxeZypSA6DiBcUa1dFGw

Additional information it will be great if validator.contramap supported option transformation:

None => Validator.pass
Some(enum) => schema.validator
adamw commented 7 months ago

I'm not sure what you're trying to achieve, but I think there are two ways. In your code above you explicitly use .get in the .map invocation, so that's what is being used.

Option 1 is to use the macro-based derivation:

case class LongString(@validate(Validator.minLength(3)) v: String)
  case class OptionWrapper[A](opt: Option[A])

  val s = implicitly[Schema[OptionWrapper[LongString]]]

  println(s.applyValidation(OptionWrapper(Some(LongString("aaa")))))
  println(s.applyValidation(OptionWrapper(Some(LongString("aa")))))
  println(s.applyValidation(OptionWrapper(None)))

this prints (as expected)

List()
List(ValidationError(MinLength(3,false),aa,List(FieldName(opt,opt), FieldName(v,v)),None))
List()

Option 2 is more manual, and involves mirroring what implicit Option schema derivation does - constructing an SOption schema, with the provided toOption method:

val longStringSchema = Schema.schemaForString.validate(Validator.minLength(3))
  val longStringWrappedSchema: Schema[OptionWrapper[String]] = Schema(
    schemaType = SOption(longStringSchema)(_.opt),
    isOptional = true,
    format = longStringSchema.format
  )