Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Validator reference is already set to Product #946

Closed
DenisNovac opened this issue Jan 17, 2021 · 5 comments
Closed

[BUG] Validator reference is already set to Product #946

DenisNovac opened this issue Jan 17, 2021 · 5 comments

Comments

@DenisNovac
Copy link
Contributor

DenisNovac commented Jan 17, 2021

Tapir version: 0.17.2

Scala version: 2.13.4

Describe the bug

Some strange parts of the code fails with error:

tapir-test[ERROR] Exception in thread "main" java.lang.ExceptionInInitializerError
tapir-test[ERROR]       at Endpoints.<init>(Endpoints.scala:8)
tapir-test[ERROR]       at Main$.<clinit>(Main.scala:13)
tapir-test[ERROR]       at Main.main(Main.scala)
tapir-test[ERROR] Caused by: java.lang.IllegalArgumentException: Validator reference is already set to Product(Map(friends -> sttp.tapir.generic.internal.SchemaMagnoliaDerivation$$anon$1@6e2c9341))!
tapir-test[ERROR]       at sttp.tapir.Validator$Ref.set(Validator.scala:228)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$combine$2(SchemaMagnoliaDerivation.scala:33)
tapir-test[ERROR]       at scala.Option.foreach(Option.scala:437)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$combine$1(SchemaMagnoliaDerivation.scala:33)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.withProgressCache(SchemaMagnoliaDerivation.scala:76)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine(SchemaMagnoliaDerivation.scala:16)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine$(SchemaMagnoliaDerivation.scala:15)
tapir-test[ERROR]       at Animal$.combine(Animal.scala:17)
tapir-test[ERROR]       at Animal$.wildcatTypeclass$macro$117$lzycompute$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.wildcatTypeclass$macro$117$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.$anonfun$schema$12(Animal.scala:27)
tapir-test[ERROR]       at magnolia.CallByNeed.value$lzycompute(magnolia.scala:818)
tapir-test[ERROR]       at magnolia.CallByNeed.value(magnolia.scala:817)
tapir-test[ERROR]       at magnolia.Subtype$$anon$1.typeclass(interface.scala:73)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$dispatch$1(SchemaMagnoliaDerivation.scala:101)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.$anonfun$map$1(ArraySeq.scala:71)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.$anonfun$map$1$adapted(ArraySeq.scala:71)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq$.tabulate(ArraySeq.scala:286)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq$.tabulate(ArraySeq.scala:265)
tapir-test[ERROR]       at scala.collection.ClassTagIterableFactory$AnyIterableDelegate.tabulate(Factory.scala:679)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.map(ArraySeq.scala:71)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.map(ArraySeq.scala:35)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.dispatch(SchemaMagnoliaDerivation.scala:101)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.dispatch$(SchemaMagnoliaDerivation.scala:100)
tapir-test[ERROR]       at Animal$.dispatch(Animal.scala:17)
tapir-test[ERROR]       at Animal$.petTypeclass$macro$108$lzycompute$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.petTypeclass$macro$108$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.paramTypeclass$macro$107$lzycompute$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.paramTypeclass$macro$107$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.$anonfun$schema$20(Animal.scala:27)
tapir-test[ERROR]       at magnolia.CallByNeed.value$lzycompute(magnolia.scala:818)
tapir-test[ERROR]       at magnolia.CallByNeed.value(magnolia.scala:817)
tapir-test[ERROR]       at magnolia.ReadOnlyParam$$anon$2.typeclass(interface.scala:173)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$productValidator$1(SchemaMagnoliaDerivation.scala:112)
tapir-test[ERROR]       at scala.collection.immutable.List.flatMap(List.scala:293)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.productValidator(SchemaMagnoliaDerivation.scala:111)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$combine$1(SchemaMagnoliaDerivation.scala:25)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.withProgressCache(SchemaMagnoliaDerivation.scala:76)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine(SchemaMagnoliaDerivation.scala:16)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine$(SchemaMagnoliaDerivation.scala:15)
tapir-test[ERROR]       at sttp.tapir.Schema$.combine(Schema.scala:99)
tapir-test[ERROR]       at Animal$.catTypeclass$macro$104$lzycompute$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.catTypeclass$macro$104$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.$anonfun$schema$1(Animal.scala:27)
tapir-test[ERROR]       at magnolia.CallByNeed.value$lzycompute(magnolia.scala:818)
tapir-test[ERROR]       at magnolia.CallByNeed.value(magnolia.scala:817)
tapir-test[ERROR]       at magnolia.Subtype$$anon$1.typeclass(interface.scala:73)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$dispatch$1(SchemaMagnoliaDerivation.scala:101)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.$anonfun$map$1(ArraySeq.scala:71)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.$anonfun$map$1$adapted(ArraySeq.scala:71)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq$.tabulate(ArraySeq.scala:286)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq$.tabulate(ArraySeq.scala:265)
tapir-test[ERROR]       at scala.collection.ClassTagIterableFactory$AnyIterableDelegate.tabulate(Factory.scala:679)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.map(ArraySeq.scala:71)
tapir-test[ERROR]       at scala.collection.immutable.ArraySeq.map(ArraySeq.scala:35)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.dispatch(SchemaMagnoliaDerivation.scala:101)
tapir-test[ERROR]       at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.dispatch$(SchemaMagnoliaDerivation.scala:100)
tapir-test[ERROR]       at sttp.tapir.Schema$.dispatch(Schema.scala:99)
tapir-test[ERROR]       at Animal$.animalTypeclass$macro$101$lzycompute$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.animalTypeclass$macro$101$1(Animal.scala:27)
tapir-test[ERROR]       at Animal$.<clinit>(Animal.scala:27)

How to reproduce?

Project with example (specific branch in repo for it): https://github.com/DenisNovac/tapir-schema-test/tree/multilevel-traits-adt

Animal (https://github.com/DenisNovac/tapir-schema-test/blob/multilevel-traits-adt/src/main/scala/Animal.scala):

sealed trait Animal {
  def name: String
}

sealed trait Pet extends Animal {
  def name: String
  def favToy: String
}

object Animal extends AutoDerivation with SchemaDerivation {

  implicit val customConfig: CirceConfiguration =
    CirceConfiguration.default.withDefaults.withDiscriminator("type")

  implicit val codec: Codec[Animal] = deriveConfiguredCodec

  implicit val tapirConfig: TapirConfiguration = TapirConfiguration.default.withDiscriminator("type")

  // It can't be implicit since recursive derivation fails
  val schema: Schema[Animal] = Schema.derived

  case class Tiger(name: String)                                                      extends Animal
  case class Elephant(name: String)                                                   extends Animal
  case class Dog(name: String, favToy: String, training: Boolean, friends: List[Pet]) extends Pet
  case class Cat(name: String, favToy: String, friends: List[Pet])                    extends Pet

  /* Works */

  //case class WildCat(name: String, friends: List[Animal])                             extends Animal

  /* Fails */

  // this is copy of Cat class but still fails
  //case class WildCat(name: String, favToy: String, friends: List[Pet]) extends Pet

  // this one is the most useful i guess
  case class WildCat(name: String, favToy: String, friends: List[Animal]) extends Pet

  //case class WildCat(name: String, friends: List[Pet]) extends Animal
  //case class WildCat(name: String, favToy: String, street: String, friends: List[Pet]) extends Pet
  //case class WildCat(name: String, favToy: String, friends: List[Pet], street: String) extends Pet

}
@DenisNovac
Copy link
Contributor Author

Actually it happens even without multiple traits:

sealed trait Animal {
  def name: String
}

object Animal extends AutoDerivation with SchemaDerivation {

  implicit val customConfig: CirceConfiguration =
    CirceConfiguration.default.withDefaults.withDiscriminator("type")

  implicit val codec: Codec[Animal] = deriveConfiguredCodec

  implicit val tapirConfig: TapirConfiguration = TapirConfiguration.default.withDiscriminator("type")

  // It can't be implicit since recursive derivation fails
  val schema: Schema[Animal] = Schema.derived

  case class Tiger(name: String)                                                         extends Animal
  case class Elephant(name: String)                                                      extends Animal
  case class Dog(name: String, favToy: String, training: Boolean, friends: List[Animal]) extends Animal
  case class Cat(name: String, favToy: String, friends: List[Animal])                    extends Animal

  //Fails:

  //case class Mouse(name: String, favToy: String, friends: List[Animal])   extends Animal
  //case class Mouse(name: String, favToy: String, friends: List[Animal], flag: Boolean)   extends Animal

}

@DenisNovac DenisNovac changed the title [BUG] Validator reference is already set to Product with ADT with multiple sealed traits at the top [BUG] Validator reference is already set to Product when case classes have the same bodies Jan 18, 2021
@DenisNovac DenisNovac changed the title [BUG] Validator reference is already set to Product when case classes have the same bodies [BUG] Validator reference is already set to Product Jan 18, 2021
@DenisNovac
Copy link
Contributor Author

Somehow it even allows to have two same case classes but fails on third.

final case class Tiger(name: String)                        extends Animal
final case class Elephant(name: String)                     extends Animal
final case class Dog(name: String, friends: List[Animal])   extends Animal
final case class Cat(name: String, friends: List[Animal])   extends Animal
final case class Mouse(name: String, friends: List[Animal]) extends Animal // it fails only with this line

@adamw
Copy link
Member

adamw commented Jan 18, 2021

The derivation cache wasn't cleared at one point. A fix is coming :)

@DenisNovac
Copy link
Contributor Author

@adamw that's great, thank you

@adamw adamw closed this as completed in aacbea7 Jan 18, 2021
@adamw
Copy link
Member

adamw commented Jan 18, 2021

Released in 0.17.5

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

No branches or pull requests

2 participants