Skip to content

Commit

Permalink
Added missing Schema/Codec for cats NonEmptyVector to integration mod…
Browse files Browse the repository at this point in the history
…ule (#3612)
  • Loading branch information
kastoestoramadus committed Mar 15, 2024
1 parent f91684b commit f166f16
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package sttp.tapir.integ.cats

import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptySet}
import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptySet, NonEmptyVector}
import sttp.tapir._
import sttp.tapir.integ.cats.ValidatorCats.nonEmptyFoldable
import sttp.tapir.Validator.nonEmpty
Expand All @@ -13,6 +13,10 @@ trait TapirCodecCats {
Schema[NonEmptyList[T]](SchemaType.SArray(implicitly[Schema[T]])(_.toList))
.validate(nonEmptyFoldable)

implicit def schemaForNev[T: Schema]: Schema[NonEmptyVector[T]] =
Schema[NonEmptyVector[T]](SchemaType.SArray(implicitly[Schema[T]])(_.toVector))
.validate(ValidatorCats.nonEmptyFoldable)

implicit def schemaForChain[T: Schema]: Schema[Chain[T]] =
implicitly[Schema[List[T]]].map(l => Option(Chain.fromSeq(l)))(_.toList)

Expand All @@ -29,6 +33,11 @@ trait TapirCodecCats {
.validate(nonEmpty)
.mapDecode { l => DecodeResult.fromOption(NonEmptyList.fromList(l)) }(_.toList)

implicit def codecForNonEmptyVector[L, H, CF <: CodecFormat](implicit c: Codec[L, Vector[H], CF]): Codec[L, NonEmptyVector[H], CF] =
c.schema(_.copy(isOptional = false))
.validate(Validator.nonEmpty)
.mapDecode { v => DecodeResult.fromOption(NonEmptyVector.fromVector(v)) }(_.toVector)

implicit def codecForChain[L, H, CF <: CodecFormat](implicit c: Codec[L, List[H], CF]): Codec[L, Chain[H], CF] =
c.map(Chain.fromSeq(_))(_.toList)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package sttp.tapir.integ.cats

import cats.data.{NonEmptyChain, NonEmptyList, NonEmptySet}
import cats.data.{NonEmptyChain, NonEmptyList, NonEmptySet, NonEmptyVector}
import org.scalacheck.{Arbitrary, Gen}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
Expand All @@ -19,6 +19,9 @@ class TapirCodecCatsTest extends AnyFlatSpec with Matchers with Checkers {
implicitly[Schema[NonEmptyList[String]]].schemaType shouldBe SArray[NonEmptyList[String], String](Schema(SString()))(_.toList)
implicitly[Schema[NonEmptyList[String]]].isOptional shouldBe false

implicitly[Schema[NonEmptyVector[String]]].schemaType shouldBe SArray[NonEmptyVector[String], String](Schema(SString()))(_.toVector)
implicitly[Schema[NonEmptyVector[String]]].isOptional shouldBe false

implicitly[Schema[NonEmptySet[String]]].schemaType shouldBe SArray[NonEmptySet[String], String](Schema(SString()))(_.toSortedSet)
implicitly[Schema[NonEmptySet[String]]].isOptional shouldBe false

Expand All @@ -32,13 +35,19 @@ class TapirCodecCatsTest extends AnyFlatSpec with Matchers with Checkers {
def expectedValidator[C[X] <: Iterable[X]] = schemaForTest.asIterable[C].validate(Validator.minSize(1))

implicitly[Schema[NonEmptyList[Test]]].showValidators shouldBe expectedValidator[List].showValidators
implicitly[Schema[NonEmptyVector[Test]]].showValidators shouldBe expectedValidator[List].showValidators
implicitly[Schema[NonEmptySet[Test]]].showValidators shouldBe expectedValidator[List].showValidators
implicitly[Schema[NonEmptyChain[Test]]].showValidators shouldBe expectedValidator[Set].showValidators
}

implicit def arbitraryNonEmptyList[T: Arbitrary]: Arbitrary[NonEmptyList[T]] =
Arbitrary(
Gen.nonEmptyListOf(implicitly[Arbitrary[T]].arbitrary).map(NonEmptyList.fromListUnsafe(_))
Gen.nonEmptyListOf(implicitly[Arbitrary[T]].arbitrary).map(NonEmptyList.fromListUnsafe)
)

implicit def arbitraryNonEmptyVector[T: Arbitrary]: Arbitrary[NonEmptyVector[T]] =
Arbitrary(
Gen.nonEmptyListOf(implicitly[Arbitrary[T]].arbitrary).map(_.toVector).map(NonEmptyVector.fromVectorUnsafe)
)

implicit def arbitraryNonEmptyChain[T: Arbitrary]: Arbitrary[NonEmptyChain[T]] =
Expand All @@ -64,43 +73,70 @@ class TapirCodecCatsTest extends AnyFlatSpec with Matchers with Checkers {

it should "fail on empty list" in {
val codecForNel = implicitly[Codec[List[String], NonEmptyList[String], CodecFormat.TextPlain]]
codecForNel.decode(List()) shouldBe DecodeResult.Missing
codecForNel.decode(Nil) shouldBe DecodeResult.Missing
}

it should "have the proper schema" in {
it should "have the proper schema for list" in {
val codecForNel = implicitly[Codec[List[String], NonEmptyList[String], CodecFormat.TextPlain]]
codecForNel.schema.copy(validator = Validator.pass) shouldBe implicitly[Schema[NonEmptyList[String]]].copy(validator = Validator.pass)
codecForNel.schema.validator.show shouldBe implicitly[Schema[NonEmptyList[String]]].validator.show
}

it should "have the proper validator" in {
it should "have the proper validator for list" in {
val codecForNel = implicitly[Codec[List[String], NonEmptyList[String], CodecFormat.TextPlain]]
codecForNel.schema.showValidators shouldBe implicitly[Schema[NonEmptyList[String]]].showValidators
}

"Provided PlainText coder for non empty vector" should "correctly serialize a non empty vector" in {
val codecForNev = implicitly[Codec[List[String], NonEmptyVector[String], CodecFormat.TextPlain]]
val rawCodec = implicitly[Codec[List[String], Vector[String], CodecFormat.TextPlain]]
check((a: NonEmptyVector[String]) => codecForNev.encode(a) == rawCodec.encode(a.toVector))
}

it should "correctly deserialize everything it serialize for vector" in {
val codecForNev = implicitly[Codec[List[String], NonEmptyVector[String], CodecFormat.TextPlain]]
check((a: NonEmptyVector[String]) => codecForNev.decode(codecForNev.encode(a)) == DecodeResult.Value(a))
}

it should "fail on empty vector" in {
val codecForNev = implicitly[Codec[List[String], NonEmptyVector[String], CodecFormat.TextPlain]]
codecForNev.decode(Nil) shouldBe DecodeResult.Missing
}

it should "have the proper schema for vector" in {
val codecForNev = implicitly[Codec[List[String], NonEmptyVector[String], CodecFormat.TextPlain]]
codecForNev.schema.copy(validator = Validator.pass) shouldBe implicitly[Schema[NonEmptyVector[String]]].copy(validator = Validator.pass)
codecForNev.schema.validator.show shouldBe implicitly[Schema[NonEmptyVector[String]]].validator.show
}

it should "have the proper validator for vector" in {
val codecForNev = implicitly[Codec[List[String], NonEmptyVector[String], CodecFormat.TextPlain]]
codecForNev.schema.showValidators shouldBe implicitly[Schema[NonEmptyVector[String]]].showValidators
}

"Provided PlainText codec for non empty chain" should "correctly serialize a non empty chain" in {
val codecForNec = implicitly[Codec[List[String], NonEmptyChain[String], CodecFormat.TextPlain]]
val rawCodec = implicitly[Codec[List[String], List[String], CodecFormat.TextPlain]]
check((a: NonEmptyChain[String]) => codecForNec.encode(a) == rawCodec.encode(a.toNonEmptyList.toList))
}

it should "correctly deserialize everything it serialize" in {
it should "correctly deserialize everything it serialize for chain" in {
val codecForNec = implicitly[Codec[List[String], NonEmptyChain[String], CodecFormat.TextPlain]]
check((a: NonEmptyChain[String]) => codecForNec.decode(codecForNec.encode(a)) == DecodeResult.Value(a))
}

it should "fail on empty list" in {
it should "fail on empty chain" in {
val codecForNec = implicitly[Codec[List[String], NonEmptyChain[String], CodecFormat.TextPlain]]
codecForNec.decode(List()) shouldBe DecodeResult.Missing
codecForNec.decode(Nil) shouldBe DecodeResult.Missing
}

it should "have the proper schema" in {
it should "have the proper schema for chain" in {
val codecForNec = implicitly[Codec[List[String], NonEmptyChain[String], CodecFormat.TextPlain]]
codecForNec.schema.copy(validator = Validator.pass) shouldBe implicitly[Schema[NonEmptyChain[String]]].copy(validator = Validator.pass)
codecForNec.schema.validator.show shouldBe implicitly[Schema[NonEmptyChain[String]]].validator.show
}

it should "have the proper validator" in {
it should "have the proper validator for chain" in {
val codecForNec = implicitly[Codec[List[String], NonEmptyChain[String], CodecFormat.TextPlain]]
codecForNec.schema.showValidators shouldBe implicitly[Schema[NonEmptyChain[String]]].showValidators
}
Expand All @@ -111,7 +147,7 @@ class TapirCodecCatsTest extends AnyFlatSpec with Matchers with Checkers {
check((a: NonEmptySet[String]) => codecForNes.encode(a) == rawCodec.encode(a.toSortedSet))
}

it should "correctly deserialize everything it serialize" in {
it should "correctly deserialize everything it serialize for set" in {
val codecForNes = implicitly[Codec[List[String], NonEmptySet[String], CodecFormat.TextPlain]]
check((a: NonEmptySet[String]) => codecForNes.decode(codecForNes.encode(a)) == DecodeResult.Value(a))
}
Expand All @@ -121,13 +157,13 @@ class TapirCodecCatsTest extends AnyFlatSpec with Matchers with Checkers {
codecForNes.decode(Nil) shouldBe DecodeResult.Missing
}

it should "have the proper schema" in {
it should "have the proper schema for set" in {
val codecForNes = implicitly[Codec[List[String], NonEmptySet[String], CodecFormat.TextPlain]]
codecForNes.schema.copy(validator = Validator.pass) shouldBe implicitly[Schema[NonEmptySet[String]]].copy(validator = Validator.pass)
codecForNes.schema.validator.show shouldBe implicitly[Schema[NonEmptySet[String]]].validator.show
}

it should "have the proper validator" in {
it should "have the proper validator for set" in {
val codecForNes = implicitly[Codec[List[String], NonEmptySet[String], CodecFormat.TextPlain]]
codecForNes.schema.showValidators shouldBe implicitly[Schema[NonEmptySet[String]]].showValidators
}
Expand Down

0 comments on commit f166f16

Please sign in to comment.