Custom Tapir Schema for self-recursive ADT

I’ve got a rather simple structure that I have to expose as an API, initially I thought of defining these as just case classes but I ended up with over 60 different case classes and that is a pain to test and to generate data for.

I have created this scastie to see the code: Scastie - An interactive playground for Scala. but here’s a little snippet:

final case class Sport(id: Int, name: Option[String]) derives Codec, Schema
final case class Competition(id: Int, name: Option[String]) derives Codec, Schema

type TradingRestriction = Restriction[SportRestriction]
type SportRestriction = Node[Sport, Competition]

enum Restriction[A]:
  case All()
  case Selections(values: NonEmptySet[A])

final case class Node[A, B](value: A, children: Restriction[B])

The problem I have is that in the NodeSchema I can’t flatten the children into separate fields in the Node. Do you have any suggestions?

If I understand correctly, you want to “pull up” all the fields from children to the node’s schema, without the intermediate children wrapper?

If so, I think the way to do it is to manipulate the schemas themselves. So: you take the node schema:

Schema
    .derived[Node[A, B]]
    .name(Schema.SName(s"${key.capitalize}Node"))

Then you change it’s SchemaType field (it’s a case class), which will be a SchemType.SProduct. You need to append the fields from the children field to the top-level. Some not-so-pretty case class manipulation should do the trick :slight_smile:

1 Like