I have created tapir endpoint:
val getEndpoint = endpoint.get
.securityIn(auth.bearer[String]())
.in("players" / path[PlayerId]("playerId"))
.in(query[PlayerRequest]("query"))
.errorOut(someErrors)
Now, I would like to read all passed values: bearer token
, playerId
and query
. So I created ZIO
server logic:
PlayersEndpoint.getEndpoint.zServerLogic { case (playerId, query) =>
//some logic to do...
}
It works fine, but whithout bearer token
. Here I could not read bearer token
. I tried to change it to something like:
PlayersEndpoint.getEndpoint.zServerSecurityLogic{ case (token) =>
//do smth with token
}.zServerLogic { case (playerId, query) =>
//some logic to do...
}
But it did not work. I would like to read all 3 values and decide about what to do after checking token. Docs and examples are very poor and do not show how to read tokens from tapir. Do you know how I should do it correctly?
CodePudding user response:
You can try this:
import org.http4s.blaze.server.BlazeServerBuilder
import org.http4s.server.Router
import sttp.tapir.server.http4s.ztapir.ZHttp4sServerInterpreter
import sttp.tapir.ztapir._
import zio._
import zio.interop.catz._
import cats.implicits._
object TapirExample extends ZIOAppDefault {
type Env = Any
// fake type
type PlayerId = Int
type PlayerRequest = Long
def authLogic(token: String): ZIO[Any, String, String] = {
if (token != "secret") ZIO.fail("user not login")
else ZIO.succeed(token)
}
val authEndpoint: ZPartialServerEndpoint[Any, String, String, Unit, String, Unit, Any] =
endpoint
.securityIn(auth.bearer[String]())
.errorOut(stringBody)
.zServerSecurityLogic(authLogic)
val getEndpoint =
authEndpoint
.get
.in("players" / path[PlayerId]("playerId"))
.in(query[PlayerRequest]("query"))
.out(stringBody)
.serverLogic(token => queryTuple => getLogic(token, queryTuple._1, queryTuple._2))
val getRoute =
ZHttp4sServerInterpreter()
.from(
List(
getEndpoint.widen[Env]
)
).toRoutes
def getLogic(token: String, playerId: PlayerId, request: PlayerRequest): ZIO[Any, Nothing, String] = {
ZIO.succeed(
s"""
| token: $token
| id: $playerId
| request: $request
|""".stripMargin)
}
override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = {
val io = ZIO.runtime[Env].flatMap { _ =>
for {
_ <- ZIO.executor.flatMap(executor =>
BlazeServerBuilder[ZIO[Env, Throwable, *]]
.withExecutionContext(executor.asExecutionContext)
.bindHttp(9090, "0.0.0.0")
.withHttpApp(
Router(
"/" -> (
getRoute
)
).orNotFound
)
.serve
.compile
.drain)
} yield ()
}
io
}
}
And the library version is
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "2.13.8"
lazy val root = (project in file("."))
.settings(
name := "playground"
)
val tapirVersion = "1.2.4"
val allDependency = Seq(
"com.softwaremill.sttp.tapir" %% "tapir-http4s-server-zio" % tapirVersion,
"org.http4s" %% "http4s-blaze-server" % "0.23.13",
)
libraryDependencies = allDependency
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full)
Test by curl
$ curl --location --request GET 'localhost:9090/players/233?query=334' \
--header 'Authorization: Bearer wrongtoken'
user not login
$ curl --location --request GET 'localhost:9090/players/233?query=334' \
--header 'Authorization: Bearer secret'
token: secret
id: 233
request: 334
refer:
- https://tapir.softwaremill.com/en/latest/server/logic.html#re-usable-security-logic
- https://github.com/softwaremill/tapir/blob/5332cf64d2ae912146e281e759b750d21bd1db55/examples/src/main/scala/sttp/tapir/examples/ZioPartialServerLogicHttp4s.scala#L22-L39
CodePudding user response:
I don't have IDE to give you the exact code but the idea is that the return of your zServerSecurityLogic
can be used in the following zServerLogic
.
Usually you'd validate the token in the zServerSecurityLogic
and return some kind of User
value that you can use as input in zServerLogic
.
But you can also "do nothing" in the security logic and just passthrough the token so that it's available in the main logic.