The Tapir documentation states that it supports decoding sealed traits: https://tapir.softwaremill.com/en/latest/endpoint/customtypes.html#sealed-traits-coproducts
However, when I try to do so using this code, I get the following error:
import io.circe.generic.auto._
import sttp.client3._
import sttp.tapir.{Schema, _}
import sttp.tapir.client.sttp._
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe._
object TmpApp extends App {
sealed trait Result {
def status: String
}
final case class IpInfo(
query: String,
country: String,
regionName: String,
city: String,
lat: Float,
lon: Float,
isp: String,
org: String,
as: String,
asname: String
) extends Result {
def status: String = "success"
}
final case class Fail(message: String, query: String) extends Result {
def status: String = "fail"
}
val sIpInfo = Schema.derive[IpInfo]
val sFail = Schema.derive[Fail]
implicit val sResult: Schema[Result] =
Schema.oneOfUsingField[Result, String](_.status, _.toString)("success" -> sIpInfo, "fail" -> sFail)
val apiEndpoint = endpoint.get
.in("batch")
.in(query[String]("fields"))
.in(jsonBody[List[String]])
.out(jsonBody[List[Result]])
.errorOut(stringBody)
val backend: SttpBackend[Identity, Any] = HttpURLConnectionBackend()
apiEndpoint
.toSttpRequestUnsafe(uri"http://ip-api.com/")
.apply(("4255449", List(
"127.0.0.1"
)))
.send(backend)
.body
}
Exception in thread "main" java.lang.IllegalArgumentException: Cannot decode from [{"status":"fail","message":"reserved range","query":"127.0.0.1"}] of request GET http://ip-api.com//batch?fields=4255449
at sttp.tapir.client.sttp.EndpointToSttpClient.$anonfun$toSttpRequest$7(EndpointToSttpClient.scala:42)
at sttp.client3.ResponseAs.$anonfun$map$1(ResponseAs.scala:27)
at sttp.client3.MappedResponseAs.$anonfun$mapWithMetadata$1(ResponseAs.scala:89)
at sttp.client3.MappedResponseAs.$anonfun$mapWithMetadata$1(ResponseAs.scala:89)
at sttp.client3.internal.BodyFromResponseAs.$anonfun$doApply$2(BodyFromResponseAs.scala:23)
at sttp.client3.monad.IdMonad$.map(IdMonad.scala:8)
at sttp.monad.syntax$MonadErrorOps.map(MonadError.scala:42)
at sttp.client3.internal.BodyFromResponseAs.doApply(BodyFromResponseAs.scala:23)
at sttp.client3.internal.BodyFromResponseAs.$anonfun$apply$1(BodyFromResponseAs.scala:13)
at sttp.monad.syntax$MonadErrorOps.map(MonadError.scala:42)
at sttp.client3.internal.BodyFromResponseAs.apply(BodyFromResponseAs.scala:13)
at sttp.client3.HttpURLConnectionBackend.readResponse(HttpURLConnectionBackend.scala:243)
at sttp.client3.HttpURLConnectionBackend.$anonfun$send$1(HttpURLConnectionBackend.scala:57)
at scala.util.Try$.apply(Try.scala:210)
at sttp.monad.MonadError.handleError(MonadError.scala:14)
at sttp.monad.MonadError.handleError$(MonadError.scala:13)
at sttp.client3.monad.IdMonad$.handleError(IdMonad.scala:6)
at sttp.client3.SttpClientException$.adjustExceptions(SttpClientException.scala:56)
at sttp.client3.HttpURLConnectionBackend.adjustExceptions(HttpURLConnectionBackend.scala:293)
at sttp.client3.HttpURLConnectionBackend.send(HttpURLConnectionBackend.scala:31)
at sttp.client3.HttpURLConnectionBackend.send(HttpURLConnectionBackend.scala:23)
at sttp.client3.FollowRedirectsBackend.sendWithCounter(FollowRedirectsBackend.scala:22)
at sttp.client3.FollowRedirectsBackend.send(FollowRedirectsBackend.scala:17)
at sttp.client3.RequestT.send(RequestT.scala:299)
at onlinenslookup.ipapi.TmpApp$.delayedEndpoint$onlinenslookup$ipapi$TmpApp$1(TmpApp.scala:53)
at onlinenslookup.ipapi.TmpApp$delayedInit$body.apply(TmpApp.scala:11)
at scala.Function0.apply$mcV$sp(Function0.scala:39)
at scala.Function0.apply$mcV$sp$(Function0.scala:39)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
at scala.App.$anonfun$main$1(App.scala:73)
at scala.App.$anonfun$main$1$adapted(App.scala:73)
at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:553)
at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:551)
at scala.collection.AbstractIterable.foreach(Iterable.scala:920)
at scala.App.main(App.scala:73)
at scala.App.main$(App.scala:71)
at onlinenslookup.ipapi.TmpApp$.main(TmpApp.scala:11)
at onlinenslookup.ipapi.TmpApp.main(TmpApp.scala)
Caused by: DecodingFailure(CNil, List(DownArray))
Process finished with exit code 1
build.sbt:
"com.softwaremill.sttp.tapir" %% "tapir-core" % "0.17.0-M10",
"com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "0.17.0-M10",
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "0.17.0-M10",
The documentation for this specific endpoint can be found here: https://ip-api.com/docs/api:batch
The decoding is delegated to Circe. What is described in the documentation is only derivation of Schema
s - which are necessary for documentation.
Hence, I'd be looking for the cause of the error by checking if you have the proper Decoder
in scope, and checking what happens if you try to decode an example value directly using circe.