From 136ab4be797f00f092a41a714c0808a3136bcccf Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 26 Sep 2023 09:50:32 +0200 Subject: [PATCH 1/2] chore: update play to 2.9-RC --- project/Dependencies.scala | 2 +- project/plugins.sbt | 2 +- readme.md | 60 ++++++++++++------- smithy4playTest/conf/application.conf | 2 +- smithy4playTest/test/TestControllerTest.scala | 10 ++-- 5 files changed, 46 insertions(+), 30 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 76e9129b..35ad17b9 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,7 +3,7 @@ import sbt._ object Dependencies { - val playVersion = "2.8.20" + val playVersion = "2.9.0-RC2" val typesafePlay = "com.typesafe.play" %% "play" % playVersion val scalaVersion = "2.13.12" diff --git a/project/plugins.sbt b/project/plugins.sbt index 8306f6ee..ecc11fdd 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,7 +2,7 @@ addSbtPlugin("com.codecommit" %% "sbt-github-packages" % "0.5.3") addSbtPlugin("org.wartremover" %% "sbt-wartremover" % "3.1.4") addSbtPlugin("org.scalameta" %% "sbt-scalafmt" % "2.5.2") addSbtPlugin("com.disneystreaming.smithy4s" %% "smithy4s-sbt-codegen" % "0.17.19") -addSbtPlugin("com.typesafe.play" %% "sbt-plugin" % "2.8.19") +addSbtPlugin("com.typesafe.play" %% "sbt-plugin" % "2.9.0-RC2") addSbtPlugin("org.scoverage" %% "sbt-scoverage" % "2.0.9") ThisBuild / dependencyOverrides ++= Seq( diff --git a/readme.md b/readme.md index 9b074ab2..47f95761 100644 --- a/readme.md +++ b/readme.md @@ -14,10 +14,10 @@ smithy4play is a routing gernator for the play2 framework based on [smithy4s](ht plugins.sbt ```scala -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.15") -addSbtPlugin("com.codecommit" % "sbt-github-packages" % "0.5.3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.*.*") +addSbtPlugin("com.codecommit" % "sbt-github-packages" % "0.*.*") addSbtPlugin( - "com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % "0.14.2" + "com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % "0.*.*" ) ``` @@ -57,26 +57,10 @@ class PreviewController @Inject( ``` **Client** -- create Client Class -- extend the Client with the generated Scala Type and smithy4play Type ClientResponse -- implement a smithy4play RequestClient that handles the request - -```scala -class PreviewControllerClient( - additionalHeaders: Map[String, Seq[String]] = Map.empty, - baseUri: String = "/") - (implicit ec: ExecutionContext, client: RequestClient) - extends PreviewControllerService[ClientResponse] { - - val smithyPlayClient = new SmithyPlayClient(baseUri, TestControllerService.service) - - override def preview(): ClientResponse[SimpleTestResponse] = - smithyPlayClient.send(PreviewControllerServiceGen.Preview(), Some(additionalHeaders)) -} -``` -now the methods from the client can be accessed like this: +- import the EnhancedGenericAPIClient ```scala -val previewControllerClient = new PreviewControllerClient() +import de.innfactory.smithy4play.client.GenericAPIClient.EnhancedGenericAPIClient +val previewControllerClient = PreviewControllerServiceGen.withClient(FakeRequestClient) previewControllerClient.preview() ``` For a further examples take a look at the smithy4playTest project. @@ -125,6 +109,38 @@ class ApiRouter @Inject()( -> / api.ApiRouter ``` +## Middlewares + +If you want Middlewares that run before the endpoint logic follow these steps: + +- Implement Middlewares +```scala +@Singleton +class ExampleMiddleware @Inject() (implicit + executionContext: ExecutionContext +) extends MiddlewareBase { + + override protected def skipMiddleware(r: RoutingContext): Boolean = false + + override def logic( + r: RoutingContext, + next: RoutingContext => RouteResult[EndpointResult] + ): RouteResult[EndpointResult] = + next(r) +} +``` +- Implement a MiddlewareRegistry and register your middlewares +```scala +class MiddlewareRegistry @Inject() ( + disableAbleMiddleware: DisableAbleMiddleware, + testMiddlewareImpl: TestMiddlewareImpl, + validateAuthMiddleware: ValidateAuthMiddleware +) extends MiddlewareRegistryBase { + override val middlewares: Seq[MiddlewareBase] = Seq(ExampleMiddleware) +} +``` + + ## Credits: [innFactory ❤️ Open Source](https://innfactory.de) \ No newline at end of file diff --git a/smithy4playTest/conf/application.conf b/smithy4playTest/conf/application.conf index 298076a7..22af932a 100644 --- a/smithy4playTest/conf/application.conf +++ b/smithy4playTest/conf/application.conf @@ -7,4 +7,4 @@ play.filters.enabled = [] smithy4play.autoRoutePackage = "controller" -play.http.secret.key = dkjfgherkgjerhgk \ No newline at end of file +play.http.secret.key = dkjfgherkgjerhgkdkjfgherkgjerhgkdkjfgherkgjerhgkdkjfgherkgjerhgkdkjfgherkgjerhgkdkjfgherkgjerhgkdkjfgherkgjerhgkdkjfgherkgjerhgk \ No newline at end of file diff --git a/smithy4playTest/test/TestControllerTest.scala b/smithy4playTest/test/TestControllerTest.scala index 375f0dea..7ed5affd 100644 --- a/smithy4playTest/test/TestControllerTest.scala +++ b/smithy4playTest/test/TestControllerTest.scala @@ -1,19 +1,19 @@ import controller.models.TestError import de.innfactory.smithy4play.CodecUtils import de.innfactory.smithy4play.client.GenericAPIClient.EnhancedGenericAPIClient -import de.innfactory.smithy4play.client.{ RequestClient, SmithyClientResponse } +import de.innfactory.smithy4play.client.{RequestClient, SmithyClientResponse} import de.innfactory.smithy4play.client.SmithyPlayTestUtils._ import de.innfactory.smithy4play.compliancetests.ComplianceClient import models.TestJson -import org.scalatestplus.play.{ BaseOneAppPerSuite, FakeApplicationFactory, PlaySpec } +import org.scalatestplus.play.{BaseOneAppPerSuite, FakeApplicationFactory, PlaySpec} import play.api.Application import play.api.Play.materializer import play.api.inject.guice.GuiceApplicationBuilder -import play.api.libs.json.{ Json, OWrites } -import play.api.mvc.{ AnyContentAsEmpty, Result } +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.{SimpleTestResponse, TestControllerServiceGen, TestRequestBody} import smithy4s.ByteArray import java.io.File From 6ff4fa2d61e9045c20963120fe1d6350aeb163db Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 26 Sep 2023 15:53:41 +0200 Subject: [PATCH 2/2] feat: adding status code to endpointResult --- .../innfactory/smithy4play/SmithyPlayEndpoint.scala | 12 ++++++------ .../scala/de/innfactory/smithy4play/package.scala | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/SmithyPlayEndpoint.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/SmithyPlayEndpoint.scala index 7aa042ef..3cb8b8d6 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/SmithyPlayEndpoint.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/SmithyPlayEndpoint.scala @@ -57,7 +57,7 @@ class SmithyPlayEndpoint[Alg[_[_, _, _, _, _]], F[_] <: ContextRoute[_], Op[ input <- getInput(request, metadata) endpointLogic = impl(endpoint.wrap(input)) .asInstanceOf[Kleisli[RouteResult, RoutingContext, O]] - .map(mapToEndpointResult) + .map(mapToEndpointResult(httpEp.code)) chainedMiddlewares = middleware.foldRight(endpointLogic)((a, b) => a.middleware(b.run)) res <- @@ -65,13 +65,13 @@ class SmithyPlayEndpoint[Alg[_[_, _, _, _, _]], F[_] <: ContextRoute[_], Op[ } yield res result.value.map { case Left(value) => handleFailure(value) - case Right(value) => handleSuccess(value, httpEp.code) + case Right(value) => handleSuccess(value) } } } .getOrElse(Action(NotFound("404"))) - private def mapToEndpointResult(o: O): EndpointResult = { + private def mapToEndpointResult(statusCode: Int)(o: O): EndpointResult = { val outputMetadata = outputMetadataEncoder.encode(o) val outputHeaders = outputMetadata.headers.map { case (k, v) => (k.toString.toLowerCase, v.mkString("")) @@ -90,7 +90,7 @@ class SmithyPlayEndpoint[Alg[_[_, _, _, _, _]], F[_] <: ContextRoute[_], Op[ .total .isEmpty // expect body if metadata decoder is not total val body = if (expectBody) Some(codecApi.writeToArray(codec, o)) else None - EndpointResult(body, outputHeaders) + EndpointResult(body, outputHeaders, statusCode) } private def getPathParams( @@ -189,8 +189,8 @@ class SmithyPlayEndpoint[Alg[_[_, _, _, _, _]], F[_] <: ContextRoute[_], Op[ private def handleFailure(error: ContextRouteError): Result = Results.Status(error.statusCode)(error.toJson) - private def handleSuccess(output: EndpointResult, code: Int): Result = { - val status = Results.Status(code) + private def handleSuccess(output: EndpointResult): Result = { + val status = Results.Status(output.code) val outputHeadersWithoutContentType = output.headers.-("content-type").toList val contentType = output.headers.getOrElse("content-type", "application/json") diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala index 5fcf0a77..cae5ebcc 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala @@ -25,7 +25,7 @@ package object smithy4play { type RouteResult[O] = EitherT[Future, ContextRouteError, O] type ContextRoute[O] = Kleisli[RouteResult, RoutingContext, O] - case class EndpointResult(body: Option[Array[Byte]], headers: Map[String, String]) + case class EndpointResult(body: Option[Array[Byte]], headers: Map[String, String], code: Int) private[smithy4play] case class Smithy4PlayError( message: String,