We have a coproduct where the root trait is referenced from a child. When we try to convert auto-generated tapir schema to apispec json schema the child schemas are referenced from $defs
object but not the root schema.
In tapir/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/TapirSchemaToJsonSchema.scala at 303bb741d762deb1c92639667e0dc9832304bbab · softwaremill/tapir · GitHub
the root schema reference is actually dropped.
The reference remains as a class full name and that does not work in validation where a correct URI is expected.
Is there some simple solution or workaround? It works for other recursive hierarchies where the root is not referenced. So one solution would be to introduce a root wrapper and then to remove the wrapper from the resulting Json schema (not that simple).
Example:
import com.networknt.schema.*
import com.networknt.schema.SpecVersion.VersionFlag
import com.networknt.schema.{ InputFormat, JsonSchema }
import io.circe.generic.extras.Configuration
import io.circe.{ Printer, Encoder }
import io.circe.syntax.*
import io.circe.generic.extras.semiauto.*
import sttp.apispec.circe.*
import sttp.tapir.Schema
import sttp.tapir.docs.apispec.schema.TapirSchemaToJsonSchema
import sttp.tapir.generic.auto.SchemaDerivation
object unresolved extends SchemaDerivation {
sealed trait Model
final case class SetModel(set: Set[Model]) extends Model
final case class LeafModel(num: Int) extends Model
implicit val configuration: Configuration = Configuration.default
implicit val setModelEncoder: Encoder[SetModel] = deriveConfiguredEncoder
implicit val leafModelEncoder: Encoder[LeafModel] = deriveConfiguredEncoder
implicit val modelEncoder: Encoder[Model] = Encoder.instance {
case x: SetModel => x.asJson
case x: LeafModel => x.asJson
}
def main(args: Array[String]): Unit = {
val printer: Printer = Printer.spaces2
val jsf: JsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V202012)
val svc: SchemaValidatorsConfig = SchemaValidatorsConfig.builder().build()
val schema = implicitly[Schema[Model]]
val apispecSchema = TapirSchemaToJsonSchema(schema, markOptionsAsNullable = true)
val jsonSchema = apispecSchema.asJson
val jsonSchemaString = jsonSchema.printWith(printer)
println(s"schema: $jsonSchemaString")
val sample = SetModel(Set(LeafModel(1), LeafModel(2)))
val sampleString = sample.asJson.printWith(printer)
println(s"sample = $sampleString")
val schemaValidator: JsonSchema = jsf.getSchema(jsonSchemaString, svc)
val result = schemaValidator.validate(sampleString, InputFormat.JSON, OutputFormat.DEFAULT)
println(s"result = ${result.toArray.mkString("\n")}")
}
}