Skip to content

Commit

Permalink
Merge pull request #718 from softwaremill/fix/715
Browse files Browse the repository at this point in the history
Properly pair validators with schema when a collection validator is mapped
  • Loading branch information
adamw authored Aug 17, 2020
2 parents 5c9130e + ee6f167 commit bb90125
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 23 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ lazy val tests: ProjectMatrix = (projectMatrix in file("tests"))
libraryDependencies ++= loggerDependencies
)
.jvmPlatform(scalaVersions = allScalaVersions)
.dependsOn(core, circeJson, enumeratum)
.dependsOn(core, circeJson, enumeratum, cats)

// integrations

Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/sttp/tapir/Validator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ object Validator extends ValidatorMagnoliaDerivation with ValidatorEnumMacro {
}.toList
}

case class Mapped[TT, T](wrapped: Validator[T], g: TT => T) extends Single[TT] {
//

case class Mapped[TT, T](wrapped: Validator[T], g: TT => T) extends Validator[TT] {
override def validate(t: TT): List[ValidationError[_]] = wrapped.validate(g(t))
}

//

case class All[T](validators: immutable.Seq[Validator[T]]) extends Validator[T] {
override def validate(t: T): List[ValidationError[_]] = validators.flatMap(_.validate(t)).toList

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ package object schema {

private[schema] def asSingleValidators(v: Validator[_]): Seq[Validator.Single[_]] = {
v match {
case Validator.All(validators) => validators.flatMap(asSingleValidators)
case Validator.Any(validators) => validators.flatMap(asSingleValidators)
case sv: Validator.Single[_] => List(sv)
case Validator.All(validators) => validators.flatMap(asSingleValidators)
case Validator.Any(validators) => validators.flatMap(asSingleValidators)
case Validator.Mapped(wrapped, _) => asSingleValidators(wrapped)
case sv: Validator.Single[_] => List(sv)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
openapi: 3.0.1
info:
title: Entities
version: '1.0'
paths:
/:
get:
operationId: getRoot
requestBody:
content:
application/json:
schema:
type: array
items:
type: string
enum:
- blue
- red
minItems: 1
required: true
responses:
'200':
description: ''
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import sttp.tapir.json.circe._
import sttp.tapir.openapi.circe.yaml._
import sttp.tapir.openapi.{Contact, Info, License, Server, ServerVariable}
import sttp.tapir.tests._
import sttp.tapir.codec.enumeratum._

import scala.collection.immutable.ListMap
import scala.io.Source
Expand Down Expand Up @@ -622,16 +621,34 @@ class VerifyYamlTest extends AnyFunSuite with Matchers {
actualYamlNoIndent shouldBe expectedYaml
}

test("use enum validator for array elements") {
val out_enum_array = endpoint.in("enum-test").out(jsonBody[FruitWithEnum])
val expectedYaml = loadYaml("expected_valid_enum_array.yml")
test("use enumeratum validator for array elements") {
import sttp.tapir.codec.enumeratum._

val actualYaml = List(out_enum_array).toOpenAPI(Info("Fruits", "1.0")).toYaml
val expectedYaml = loadYaml("expected_valid_enumeratum.yml")

val actualYaml = List(endpoint.in("enum-test").out(jsonBody[Enumeratum.FruitWithEnum])).toOpenAPI(Info("Fruits", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("use enum validator for a cats non-empty-list of enums") {
import sttp.tapir.integ.cats.codec._
import cats.data.NonEmptyList
implicit def schemaForColor: Schema[Color] = Schema(SchemaType.SString)
implicit def validatorForColor: Validator[Color] =
Validator.enum(List(Blue, Red), { c => Some(c.toString.toLowerCase()) })

val expectedYaml = loadYaml("expected_valid_enum_cats_nel.yml")

val actualYaml = endpoint
.in(jsonBody[NonEmptyList[Color]])
.toOpenAPI(Info("Entities", "1.0"))
.toYaml
val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("support example of list and not-list types") {
val expectedYaml = loadYaml("expected_examples_of_list_and_not_list_types.yml")
val actualYaml = endpoint.post
Expand Down
23 changes: 12 additions & 11 deletions tests/src/main/scala/sttp/tapir/tests/FruitAmount.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package sttp.tapir.tests

import enumeratum.EnumEntry
import enumeratum.Enum
import sttp.tapir._
import sttp.tapir.codec.enumeratum.TapirCodecEnumeratum

import scala.collection.immutable

Expand All @@ -17,19 +14,23 @@ case class ValidFruitAmount(fruit: StringWrapper, amount: IntWrapper)

case class ColorWrapper(color: Color)

case class FruitWithEnum(fruit: String, amount: Int, fruitType: List[FruitType])

sealed trait Entity {
def name: String
}
case class Person(name: String, age: Int) extends Entity
case class Organization(name: String) extends Entity

sealed trait FruitType extends EnumEntry
object Enumeratum {
import enumeratum.EnumEntry
import enumeratum.Enum

case class FruitWithEnum(fruit: String, amount: Int, fruitType: List[FruitType])

object FruitType extends Enum[FruitType] {
case object APPLE extends FruitType
case object PEAR extends FruitType
sealed trait FruitType extends EnumEntry

override def values: immutable.IndexedSeq[FruitType] = findValues
}
object FruitType extends Enum[FruitType] {
case object APPLE extends FruitType
case object PEAR extends FruitType
override def values: immutable.IndexedSeq[FruitType] = findValues
}
}

0 comments on commit bb90125

Please sign in to comment.