Skip to content

Commit

Permalink
Migrate play client
Browse files Browse the repository at this point in the history
  • Loading branch information
adamw committed Oct 29, 2021
1 parent 316f2a2 commit 16edd6e
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ import scala.collection.immutable.Seq

private[play] class EndpointToPlayClient(clientOptions: PlayClientOptions, ws: StandaloneWSClient) {

def toPlayRequest[I, E, O, R](
e: Endpoint[I, E, O, R],
def toPlayRequest[A, I, E, O, R](
e: Endpoint[A, I, E, O, R],
baseUri: String
): I => (StandaloneWSRequest, StandaloneWSResponse => DecodeResult[Either[E, O]]) = { params =>
val req = setInputParams(e.input, ParamsAsAny(params), ws.url(baseUri))
.withMethod(e.input.method.getOrElse(Method.GET).method)
): A => I => (StandaloneWSRequest, StandaloneWSResponse => DecodeResult[Either[E, O]]) = { aParams => iParams =>
val req0 = setInputParams(e.securityInput, ParamsAsAny(aParams), ws.url(baseUri))
val req = setInputParams(e.input, ParamsAsAny(iParams), req0)
.withMethod(e.input.method.orElse(e.securityInput.method).getOrElse(Method.GET).method)

def responseParser(response: StandaloneWSResponse): DecodeResult[Either[E, O]] = {
parsePlayResponse(e)(response) match {
Expand All @@ -50,32 +51,33 @@ private[play] class EndpointToPlayClient(clientOptions: PlayClientOptions, ws: S
(req, responseParser)
}

def toPlayRequestUnsafe[I, E, O, R](
e: Endpoint[I, E, O, R],
def toPlayRequestUnsafe[A, I, E, O, R](
e: Endpoint[A, I, E, O, R],
baseUri: String
): I => (StandaloneWSRequest, StandaloneWSResponse => Either[E, O]) = { params =>
val (req, responseParser) = toPlayRequest(e, baseUri)(params)
): A => I => (StandaloneWSRequest, StandaloneWSResponse => Either[E, O]) = { aParams => iParams =>
val (req, responseParser) = toPlayRequest(e, baseUri)(aParams)(iParams)
def unsafeResponseParser(response: StandaloneWSResponse): Either[E, O] = {
getOrThrow(responseParser(response))
}
(req, unsafeResponseParser)
}

private def parsePlayResponse[I, E, O, R](e: Endpoint[I, E, O, R]): StandaloneWSResponse => DecodeResult[Either[E, O]] = { response =>
val code = sttp.model.StatusCode(response.status)
private def parsePlayResponse[A, I, E, O, R](e: Endpoint[A, I, E, O, R]): StandaloneWSResponse => DecodeResult[Either[E, O]] = {
response =>
val code = sttp.model.StatusCode(response.status)

val parser = if (code.isSuccess) responseFromOutput(e.output) else responseFromOutput(e.errorOutput)
val output = if (code.isSuccess) e.output else e.errorOutput
val parser = if (code.isSuccess) responseFromOutput(e.output) else responseFromOutput(e.errorOutput)
val output = if (code.isSuccess) e.output else e.errorOutput

val headers = (cookiesAsHeaders(response.cookies.toVector) ++ response.headers).flatMap { case (name, values) =>
values.map { v => Header(name, v) }
}.toVector
val headers = (cookiesAsHeaders(response.cookies.toVector) ++ response.headers).flatMap { case (name, values) =>
values.map { v => Header(name, v) }
}.toVector

val meta = ResponseMetadata(code, response.statusText, headers)
val meta = ResponseMetadata(code, response.statusText, headers)

val params = clientOutputParams(output, parser(response), meta)
val params = clientOutputParams(output, parser(response), meta)

params.map(_.asAny).map(p => if (code.isSuccess) Right(p.asInstanceOf[O]) else Left(p.asInstanceOf[E]))
params.map(_.asAny).map(p => if (code.isSuccess) Right(p.asInstanceOf[O]) else Left(p.asInstanceOf[E]))
}

private def cookiesAsHeaders(cookies: Seq[WSCookie]): Map[String, Seq[String]] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
package sttp.tapir.client.play

import play.api.libs.ws.{StandaloneWSClient, StandaloneWSRequest, StandaloneWSResponse}
import sttp.tapir.{DecodeResult, Endpoint}
import sttp.tapir.{DecodeResult, Endpoint, PublicEndpoint}

trait PlayClientInterpreter {

def playClientOptions: PlayClientOptions = PlayClientOptions.default

/** Interprets the endpoint as a client call, using the given `baseUri` as the starting point to create the target uri.
// public

/** Interprets the public endpoint as a client call, using the given `baseUri` as the starting point to create the target uri.
*
* Returns:
* - a function which, when applied to the endpoint's input parameters (given as a tuple), will encode them to appropriate request
* parameters: path, query, headers and body. The result is a `StandaloneWSRequest`, which can be sent using the `execute()` method.
* - a response parser to use on the `StandaloneWSResponse` obtained after executing the request.
*/
def toRequest[I, E, O, R](e: Endpoint[I, E, O, R], baseUri: String)(implicit
def toRequest[I, E, O, R](e: PublicEndpoint[I, E, O, R], baseUri: String)(implicit
ws: StandaloneWSClient
): I => (StandaloneWSRequest, StandaloneWSResponse => DecodeResult[Either[E, O]]) =
new EndpointToPlayClient(playClientOptions, ws).toPlayRequest(e, baseUri)
new EndpointToPlayClient(playClientOptions, ws).toPlayRequest(e, baseUri).apply(())

/** Interprets the endpoint as a client call, using the given `baseUri` as the starting point to create the target uri.
/** Interprets the public endpoint as a client call, using the given `baseUri` as the starting point to create the target uri.
*
* Returns:
* - a function which, when applied to the endpoint's input parameters (given as a tuple), will encode them to appropriate request
Expand All @@ -29,11 +31,41 @@ trait PlayClientInterpreter {
* @throws IllegalArgumentException
* when response parsing fails
*/
def toRequestUnsafe[I, E, O, R](e: Endpoint[I, E, O, R], baseUri: String)(implicit
def toRequestUnsafe[I, E, O, R](e: PublicEndpoint[I, E, O, R], baseUri: String)(implicit
ws: StandaloneWSClient
): I => (StandaloneWSRequest, StandaloneWSResponse => Either[E, O]) =
new EndpointToPlayClient(playClientOptions, ws).toPlayRequestUnsafe(e, baseUri)
new EndpointToPlayClient(playClientOptions, ws).toPlayRequestUnsafe(e, baseUri).apply(())

// secure

/** Interprets the secure endpoint as a client call, using the given `baseUri` as the starting point to create the target uri.
*
* Returns:
* - a function which, when applied to the endpoint's security and regular input parameters (given as tuples), will encode them to
* appropriate request parameters: path, query, headers and body. The result is a `StandaloneWSRequest`, which can be sent using the
* `execute()` method.
* - a response parser to use on the `StandaloneWSResponse` obtained after executing the request.
*/
def toSecureRequest[A, I, E, O, R](e: Endpoint[A, I, E, O, R], baseUri: String)(implicit
ws: StandaloneWSClient
): A => I => (StandaloneWSRequest, StandaloneWSResponse => DecodeResult[Either[E, O]]) =
new EndpointToPlayClient(playClientOptions, ws).toPlayRequest(e, baseUri)

/** Interprets the secure endpoint as a client call, using the given `baseUri` as the starting point to create the target uri.
*
* Returns:
* - a function which, when applied to the endpoint's security and regular input parameters (given as tuples), will encode them to
* appropriate request parameters: path, query, headers and body. The result is a `StandaloneWSRequest`, which can be sent using the
* `execute()` method.
* - a response parser to use on the `StandaloneWSResponse` obtained after executing the request.
*
* @throws IllegalArgumentException
* when response parsing fails
*/
def toSecureRequestUnsafe[A, I, E, O, R](e: Endpoint[A, I, E, O, R], baseUri: String)(implicit
ws: StandaloneWSClient
): A => I => (StandaloneWSRequest, StandaloneWSResponse => Either[E, O]) =
new EndpointToPlayClient(playClientOptions, ws).toPlayRequestUnsafe(e, baseUri)
}

object PlayClientInterpreter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,37 @@ import play.api.libs.ws.ahc.StandaloneAhcWSClient
import sttp.tapir.client.tests.ClientTests
import sttp.tapir.{DecodeResult, Endpoint}

import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.Future

abstract class PlayClientTests[R] extends ClientTests[R] {

implicit val materializer: Materializer = Materializer(ActorSystem("tests"))

implicit val wsClient: StandaloneWSClient = StandaloneAhcWSClient()

override def send[I, E, O](e: Endpoint[I, E, O, R], port: Port, args: I, scheme: String = "http"): IO[Either[E, O]] = {
override def send[A, I, E, O](
e: Endpoint[A, I, E, O, R],
port: Port,
securityArgs: A,
args: I,
scheme: String = "http"
): IO[Either[E, O]] = {
def response: Future[Either[E, O]] = {
val (req, responseParser) = PlayClientInterpreter().toRequestUnsafe(e, s"http://localhost:$port").apply(args)
val (req, responseParser) =
PlayClientInterpreter().toSecureRequestUnsafe(e, s"http://localhost:$port").apply(securityArgs).apply(args)
req.execute().map(responseParser)
}
IO.fromFuture(IO(response))
}

override def safeSend[I, E, O](
e: Endpoint[I, E, O, R],
override def safeSend[A, I, E, O](
e: Endpoint[A, I, E, O, R],
port: Port,
securityArgs: A,
args: I
): IO[DecodeResult[Either[E, O]]] = {
def response: Future[DecodeResult[Either[E, O]]] = {
val (req, responseParser) = PlayClientInterpreter().toRequest(e, s"http://localhost:$port").apply(args)
val (req, responseParser) = PlayClientInterpreter().toSecureRequest(e, s"http://localhost:$port").apply(securityArgs).apply(args)
req.execute().map(responseParser)
}
IO.fromFuture(IO(response))
Expand Down

0 comments on commit 16edd6e

Please sign in to comment.