Skip to content

Commit

Permalink
Merge pull request #47 from innFactory/feature/clientWithHeaders
Browse files Browse the repository at this point in the history
feat: adding seperate GenericClient with possibiliy to add Headers on…
  • Loading branch information
patsta32 authored Oct 21, 2022
2 parents 6850513 + de53d0c commit 4bd5f10
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.innfactory.smithy4play.client

import de.innfactory.smithy4play.{ ClientRequest, ClientResponse }
import cats.data.Kleisli
import de.innfactory.smithy4play.{ ClientRequest, ClientResponse, RunnableClientRequest }
import smithy4s.{ Service, Transformation }

import scala.concurrent.ExecutionContext
Expand All @@ -13,24 +14,39 @@ private class GenericAPIClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](
private val smithyPlayClient = new SmithyPlayClient("/", service, client)

/* Takes a service and creates a Transformation[Op, ClientRequest] */
private def transformer(): Alg[RunnableClientRequest] =
service.transform(this.opToResponse())

private def transformer(additionalHeaders: Option[Map[String, Seq[String]]]): Alg[ClientRequest] =
service.transform(this.opToResponse(additionalHeaders))

/* uses the SmithyPlayClient to transform a Operation to a ClientResponse */
private def opToResponse(): Transformation[Op, RunnableClientRequest] =
new Transformation[Op, RunnableClientRequest] {
override def apply[I, E, O, SI, SO](fa: Op[I, E, O, SI, SO]): RunnableClientRequest[I, E, O, SI, SO] =
Kleisli[ClientResponse, Option[Map[String, Seq[String]]], O] { additionalHeaders =>
smithyPlayClient.send(fa, additionalHeaders)
}
}

private def opToResponse(additionalHeaders: Option[Map[String, Seq[String]]]): Transformation[Op, ClientRequest] =
new Transformation[Op, ClientRequest] {
def apply[I, E, O, SI, SO](op: Op[I, E, O, SI, SO]): ClientResponse[O] =
smithyPlayClient.send(op, additionalHeaders)
override def apply[I, E, O, SI, SO](fa: Op[I, E, O, SI, SO]): ClientRequest[I, E, O, SI, SO] =
smithyPlayClient.send(fa, additionalHeaders)
}
}

object GenericAPIClient {

implicit class EnhancedGenericAPIClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](service: Service[Alg, Op]) {
def withClient(
def withClientAndHeaders(
client: RequestClient,
additionalHeaders: Option[Map[String, Seq[String]]] = None
additionalHeaders: Option[Map[String, Seq[String]]]
)(implicit ec: ExecutionContext) = apply(service, additionalHeaders, client)

def withClient(
client: RequestClient
)(implicit ec: ExecutionContext) = apply(service, client)
}
def apply[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](
serviceI: Service[Alg, Op],
Expand All @@ -39,4 +55,10 @@ object GenericAPIClient {
)(implicit ec: ExecutionContext): Alg[ClientRequest] =
new GenericAPIClient(serviceI, client).transformer(additionalHeaders)

def apply[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](
serviceI: Service[Alg, Op],
client: RequestClient
)(implicit ec: ExecutionContext): Alg[RunnableClientRequest] =
new GenericAPIClient(serviceI, client).transformer()

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package de.innfactory.smithy4play.client

import de.innfactory.smithy4play.Showable

case class SmithyPlayClientEndpointErrorResponse(
error: Array[Byte],
statusCode: Int,
expectedStatusCode: Int
)
) extends Showable
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package de.innfactory.smithy4play.client

import de.innfactory.smithy4play.Showable

case class SmithyPlayClientEndpointResponse[O](
body: Option[O],
headers: Map[String, Seq[String]],
statusCode: Int,
expectedStatusCode: Int
)
) extends Showable
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.innfactory.smithy4play.client

import de.innfactory.smithy4play.{ ClientResponse, RoutingErrorResponse }
import de.innfactory.smithy4play.{ logger, ClientResponse, RoutingErrorResponse }
import play.api.libs.json.Json

import scala.concurrent.duration.{ Duration, DurationInt }
Expand All @@ -14,7 +14,10 @@ object SmithyPlayTestUtils {
timeout: Duration = 5.seconds
): SmithyPlayClientEndpointResponse[O] =
Await.result(
response.map(_.toOption.get),
response.map { res =>
if (res.isLeft) logger.error(s"Expected Right, got Left: ${res.left.toOption.get.toString}")
res.toOption.get
},
timeout
)

Expand All @@ -24,7 +27,10 @@ object SmithyPlayTestUtils {
errorAsString: Boolean = true
): SmithyPlayClientEndpointErrorResponse =
Await.result(
response.map(_.left.toOption.get),
response.map { res =>
if (res.isRight) logger.error(s"Expected Left, got Right: ${res.toOption.get.toString}")
res.left.toOption.get
},
timeout
)
}
Expand Down
34 changes: 27 additions & 7 deletions smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ package object smithy4play {
def statusCode: Int
}

type ClientResponse[O] = Future[Either[SmithyPlayClientEndpointErrorResponse, SmithyPlayClientEndpointResponse[O]]]

type ClientRequest[I, E, O, SI, SO] = ClientResponse[O]

type RouteResult[O] = EitherT[Future, ContextRouteError, O]

type ContextRoute[O] = Kleisli[RouteResult, RoutingContext, O]
type ClientResponse[O] = Future[Either[SmithyPlayClientEndpointErrorResponse, SmithyPlayClientEndpointResponse[O]]]
type ClientRequest[I, E, O, SI, SO] = ClientResponse[O]
type RunnableClientRequest[I, E, O, SI, SO] = Kleisli[ClientResponse, Option[Map[String, Seq[String]]], O]
type RouteResult[O] = EitherT[Future, ContextRouteError, O]
type ContextRoute[O] = Kleisli[RouteResult, RoutingContext, O]

private[smithy4play] case class Smithy4PlayError(
message: String,
Expand Down Expand Up @@ -59,4 +57,26 @@ package object smithy4play {
def macroTransform(annottees: Any*): Any = macro AutoRoutingMacro.impl
}

private[smithy4play] trait Showable {
this: Product =>
override def toString: String = this.show
}

private[smithy4play] object Showable {
implicit class ShowableProduct(product: Product) {
def show: String = {
val className = product.productPrefix
val fieldNames = product.productElementNames.toList
val fieldValues = product.productIterator.toList
val fields = fieldNames.zip(fieldValues).map { case (name, value) =>
value match {
case subProduct: Product => s"$name = ${subProduct.show}"
case _ => s"$name = $value"
}
}
fields.mkString(s"$className(", ", ", ")")
}
}
}

}
8 changes: 4 additions & 4 deletions smithy4playTest/test/TestControllerTest.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import de.innfactory.smithy4play.ClientRequest
import de.innfactory.smithy4play.client.GenericAPIClient.EnhancedGenericAPIClient
import de.innfactory.smithy4play.client.{ GenericAPIClient, RequestClient, SmithyClientResponse }
import de.innfactory.smithy4play.client.{ RequestClient, SmithyClientResponse }
import de.innfactory.smithy4play.client.SmithyPlayTestUtils._
import de.innfactory.smithy4play.compliancetests.ComplianceClient
import org.scalatestplus.play.{ BaseOneAppPerSuite, FakeApplicationFactory, PlaySpec }
Expand All @@ -11,7 +10,7 @@ import play.api.libs.json.{ Json, OWrites }
import play.api.mvc.{ AnyContentAsEmpty, Result }
import play.api.test.FakeRequest
import play.api.test.Helpers._
import testDefinitions.test.{ SimpleTestResponse, TestControllerServiceGen, TestRequestBody }
import testDefinitions.test.{ TestControllerServiceGen, TestRequestBody }
import smithy4s.ByteArray

import java.io.File
Expand Down Expand Up @@ -50,7 +49,8 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli
}
}

val genericClient = TestControllerServiceGen.withClient(FakeRequestClient)
val genericClient = TestControllerServiceGen.withClientAndHeaders(FakeRequestClient, None)
val client2 = TestControllerServiceGen.withClient(FakeRequestClient)

override def fakeApplication(): Application =
new GuiceApplicationBuilder().build()
Expand Down

0 comments on commit 4bd5f10

Please sign in to comment.