From b8e02622a804941095bb5ab4b89a2e102b94d2e6 Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Mon, 17 Oct 2022 15:27:20 +0200 Subject: [PATCH 1/7] upgrading smithy4s and implementing smithy4s compliance Client --- build.sbt | 9 ++- project/Dependencies.scala | 20 ++++- project/plugins.sbt | 2 +- .../compliancetests/ClientTest.scala | 79 +++++++++++++++++++ smithy4playTest/test/TestControllerTest.scala | 15 ++-- smithy4playTest/testSpecs/HttpTest.smithy | 37 +++++++++ .../testSpecs/TestController.smithy | 1 + 7 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala create mode 100644 smithy4playTest/testSpecs/HttpTest.smithy diff --git a/build.sbt b/build.sbt index 11279b15..c7bf4edd 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,6 @@ import sbt.Compile import sbt.Keys.cleanFiles - -val releaseVersion = sys.env.getOrElse("TAG", "0.2.3-BETA.2") +val releaseVersion = sys.env.getOrElse("TAG", "0.2.3-BETA.3") addCommandAlias("publishSmithy4Play", "smithy4play/publish") addCommandAlias("publishLocalSmithy4Play", "smithy4play/publishLocal") addCommandAlias("generateCoverage", "clean; coverage; test; coverageReport") @@ -32,10 +31,12 @@ val sharedSettings = defaultProjectSettings lazy val smithy4play = project .in(file("smithy4play")) + .enablePlugins(Smithy4sCodegenPlugin) .settings( sharedSettings, - scalaVersion := Dependencies.scalaVersion, - name := "smithy4play", + scalaVersion := Dependencies.scalaVersion, + Compile / smithy4sAllowedNamespaces := List("smithy.test"), + name := "smithy4play", scalacOptions += "-Ymacro-annotations", Compile / compile / wartremoverWarnings ++= Warts.unsafe, libraryDependencies ++= Dependencies.list diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 2af612aa..36c7a144 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -6,12 +6,20 @@ object Dependencies { val playVersion = "2.8.13" val typesafePlay = "com.typesafe.play" %% "play" % playVersion - val scalaVersion = "2.13.8" + val scalaVersion = "2.13.8" + val smithy4sVersion = "0.16.4" + val smithyCore = "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion + val smithyJson = "com.disneystreaming.smithy4s" %% "smithy4s-json" % smithy4sVersion - val smithyCore = "com.disneystreaming.smithy4s" %% "smithy4s-core" % "0.16.1" + val classgraph = "io.github.classgraph" % "classgraph" % "4.8.149" + val smithyVersion = "1.24.0" + val model = "software.amazon.smithy" % "smithy-model" % smithyVersion - val smithyJson = "com.disneystreaming.smithy4s" %% "smithy4s-json" % "0.16.1" - val classgraph = "io.github.classgraph" % "classgraph" % "4.8.149" + val testTraits = + "software.amazon.smithy" % "smithy-protocol-test-traits" % smithyVersion + val build = "software.amazon.smithy" % "smithy-build" % smithyVersion + val awsTraits = + "software.amazon.smithy" % "smithy-aws-traits" % smithyVersion val scalatestPlus = "org.scalatestplus.play" %% "scalatestplus-play" % "5.1.0" % Test @@ -20,6 +28,10 @@ object Dependencies { lazy val list = Seq( smithyCore, smithyJson, + model, + testTraits, + build, + awsTraits, classgraph, scalatestPlus, typesafePlay, diff --git a/project/plugins.sbt b/project/plugins.sbt index 19ab39c2..69994479 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ addSbtPlugin("com.codecommit" % "sbt-github-packages" % "0.5.3") addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.0.5") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") -addSbtPlugin("com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % "0.15.2") +addSbtPlugin("com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % "0.16.4") addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.15") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.3") diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala new file mode 100644 index 00000000..aebb4d64 --- /dev/null +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala @@ -0,0 +1,79 @@ +package de.innfactory.smithy4play.compliancetests + +import de.innfactory.smithy4play.ClientResponse +import de.innfactory.smithy4play.client.{ + SmithyPlayClientEndpointErrorResponse, + SmithyPlayClientEndpointResponse +} +import smithy.test._ +import smithy4s.http.HttpEndpoint +import smithy4s.{ Document, Endpoint, GenLift, Monadic, Service } + +import scala.concurrent.duration.DurationInt +import scala.concurrent.{ Await, ExecutionContext } + +class ClientTest[ + Alg[_[_, _, _, _, _]], + Op[_, _, _, _, _] +]( + client: Monadic[Alg, ClientResponse] +)(implicit + service: Service[Alg, Op], + ec: ExecutionContext +) { + + def clientRequest[I, E, O, SE, SO]( + endpoint: Endpoint[Op, I, E, O, SE, SO], + requestTestCase: Option[HttpRequestTestCase], + responseTestCase: Option[HttpResponseTestCase] + ): Boolean = { + + val inputFromDocument = Document.Decoder.fromSchema(endpoint.input) + val input = inputFromDocument.decode(requestTestCase.flatMap(_.params).getOrElse(Document.obj())).toOption.get + + val result = service + .asTransformation[GenLift[ClientResponse]#λ](client) + .apply(endpoint.wrap(input)) + .map(res => matchResponse(res, endpoint, responseTestCase)) + Await.result(result, 5.seconds) + } + + def matchResponse[I, E, O, SE, SO]( + response: Either[SmithyPlayClientEndpointErrorResponse, SmithyPlayClientEndpointResponse[O]], + endpoint: Endpoint[Op, I, E, O, SE, SO], + responseTestCase: Option[HttpResponseTestCase] + ) = { + + val httpEp = HttpEndpoint.cast(endpoint).get + val responseStatusCode = response match { + case Left(value) => value.statusCode + case Right(value) => value.statusCode + } + val statusAssert = responseTestCase.map(_.code).getOrElse(httpEp.code) == responseStatusCode + + val outputFromDocument = Document.Decoder.fromSchema(endpoint.output) + val expectedOutput = + outputFromDocument.decode(responseTestCase.flatMap(_.params).getOrElse(Document.obj())).toOption + + println(statusAssert + responseStatusCode.toString) + println(expectedOutput) + responseTestCase.forall(_ => expectedOutput == response.toOption.flatMap(_.body)) && statusAssert + } + + def tests(): Seq[Boolean] = + service.endpoints.flatMap { endpoint => + val requests = endpoint.hints + .get(HttpRequestTests) + .map(_.value) + .getOrElse(Nil) + val responses = endpoint.hints + .get(HttpResponseTests) + .map(_.value) + .getOrElse(Nil) + val ids = requests.map(_.id).toSet ++ responses.map(_.id).toSet + + ids + .map(id => (requests.find(_.id == id), responses.find(_.id == id))) + .map(x => clientRequest(endpoint, x._1, x._2)) + } +} diff --git a/smithy4playTest/test/TestControllerTest.scala b/smithy4playTest/test/TestControllerTest.scala index c5577181..2a40b4bd 100644 --- a/smithy4playTest/test/TestControllerTest.scala +++ b/smithy4playTest/test/TestControllerTest.scala @@ -1,11 +1,12 @@ -import de.innfactory.smithy4play.client.{RequestClient, SmithyClientResponse} +import de.innfactory.smithy4play.client.{ RequestClient, SmithyClientResponse } import de.innfactory.smithy4play.client.SmithyPlayTestUtils._ -import org.scalatestplus.play.{BaseOneAppPerSuite, FakeApplicationFactory, PlaySpec} +import de.innfactory.smithy4play.compliancetests.ClientTest +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.TestRequestBody @@ -55,6 +56,10 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli "controller.TestController" must { + "new autoTest test" in { + new ClientTest(testControllerClient).tests().map(_ mustBe true) + } + "route to Test Endpoint" in { val result = testControllerClient.test().awaitRight @@ -115,7 +120,7 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli "route to error Endpoint" in { val result = testControllerClient.testThatReturnsError().awaitLeft - result.toErrorResponse.message must include ("fail") + result.toErrorResponse.message must include("fail") result.statusCode mustBe 500 } diff --git a/smithy4playTest/testSpecs/HttpTest.smithy b/smithy4playTest/testSpecs/HttpTest.smithy new file mode 100644 index 00000000..05a6121f --- /dev/null +++ b/smithy4playTest/testSpecs/HttpTest.smithy @@ -0,0 +1,37 @@ +$version: "2" + +namespace testDefinitions.test + +use smithy4s.api#simpleRestJson +use smithy.test#httpRequestTests +use smithy.test#httpResponseTests + +apply TestWithQuery @httpRequestTests([ + { + id: "test1", + method: "GET", + uri: "/query", + protocol: simpleRestJson, + params: { + "testQuery": "Hello there" + }, + } +]) +apply TestWithQuery @httpResponseTests([ + { + id: "test1", + protocol: simpleRestJson, + code: 200 + } +]) + +apply Test @httpResponseTests([ + { + id: "test2", + protocol: simpleRestJson, + params: { + message: "TestWithSimpleResponse" + }, + code: 200 + } +]) \ No newline at end of file diff --git a/smithy4playTest/testSpecs/TestController.smithy b/smithy4playTest/testSpecs/TestController.smithy index f0af01eb..96e9941c 100644 --- a/smithy4playTest/testSpecs/TestController.smithy +++ b/smithy4playTest/testSpecs/TestController.smithy @@ -10,6 +10,7 @@ service TestControllerService { operations: [Test, TestWithOutput, Health, TestWithBlob, TestWithQuery, TestThatReturnsError] } + @http(method: "POST", uri: "/blob", code: 200) operation TestWithBlob { input: BlobRequest, From 4a9df5953577aa10a896f15b209fd92d0be77da5 Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 18 Oct 2022 12:35:49 +0200 Subject: [PATCH 2/7] adding GenericAPIClient --- .../smithy4play/client/GenericAPIClient.scala | 30 +++++++++++ .../compliancetests/ClientTest.scala | 50 ++++++++++++++----- .../app/controller/TestController.scala | 1 + smithy4playTest/test/TestControllerTest.scala | 27 ++++++++-- .../{HttpTest.smithy => 200TestSuite.smithy} | 0 smithy4playTest/testSpecs/500TestSuite.smithy | 29 +++++++++++ .../testSpecs/TestController.smithy | 11 ++++ 7 files changed, 131 insertions(+), 17 deletions(-) create mode 100644 smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala rename smithy4playTest/testSpecs/{HttpTest.smithy => 200TestSuite.smithy} (100%) create mode 100644 smithy4playTest/testSpecs/500TestSuite.smithy diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala new file mode 100644 index 00000000..fbebc3d3 --- /dev/null +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala @@ -0,0 +1,30 @@ +package de.innfactory.smithy4play.client + +import de.innfactory.smithy4play.ClientResponse +import smithy4s.{ GenLift, Service, Transformation } + +import scala.concurrent.ExecutionContext + +class GenericAPIClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]]( + service: Service[Alg, Op] +)(implicit ec: ExecutionContext, client: RequestClient) { + + val smithyPlayClient = new SmithyPlayClient("/", service) + def transformer(additionalHeaders: Option[Map[String, Seq[String]]]): Alg[GenLift[ClientResponse]#λ] = + service.transform(this.opToResponse(additionalHeaders)) + + def opToResponse(additionalHeaders: Option[Map[String, Seq[String]]]): Transformation[Op, GenLift[ClientResponse]#λ] = + new Transformation[Op, GenLift[ClientResponse]#λ] { + def apply[I, E, O, SI, SO](op: Op[I, E, O, SI, SO]): ClientResponse[O] = + smithyPlayClient.send(op, additionalHeaders) + } +} + +object GenericAPIClient { + def apply[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]]( + serviceI: Service[Alg, Op], + additionalHeaders: Option[Map[String, Seq[String]]] = None + )(implicit ec: ExecutionContext, client: RequestClient): Alg[GenLift[ClientResponse]#λ] = + new GenericAPIClient(serviceI).transformer(additionalHeaders) + +} diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala index aebb4d64..f89c5a74 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala @@ -1,10 +1,8 @@ package de.innfactory.smithy4play.compliancetests import de.innfactory.smithy4play.ClientResponse -import de.innfactory.smithy4play.client.{ - SmithyPlayClientEndpointErrorResponse, - SmithyPlayClientEndpointResponse -} +import de.innfactory.smithy4play.client.{ SmithyPlayClientEndpointErrorResponse, SmithyPlayClientEndpointResponse } +import play.api.libs.json.Json import smithy.test._ import smithy4s.http.HttpEndpoint import smithy4s.{ Document, Endpoint, GenLift, Monadic, Service } @@ -22,11 +20,11 @@ class ClientTest[ ec: ExecutionContext ) { - def clientRequest[I, E, O, SE, SO]( + private def clientRequest[I, E, O, SE, SO]( endpoint: Endpoint[Op, I, E, O, SE, SO], requestTestCase: Option[HttpRequestTestCase], responseTestCase: Option[HttpResponseTestCase] - ): Boolean = { + ) = { val inputFromDocument = Document.Decoder.fromSchema(endpoint.input) val input = inputFromDocument.decode(requestTestCase.flatMap(_.params).getOrElse(Document.obj())).toOption.get @@ -38,7 +36,7 @@ class ClientTest[ Await.result(result, 5.seconds) } - def matchResponse[I, E, O, SE, SO]( + private def matchResponse[I, E, O, SE, SO]( response: Either[SmithyPlayClientEndpointErrorResponse, SmithyPlayClientEndpointResponse[O]], endpoint: Endpoint[Op, I, E, O, SE, SO], responseTestCase: Option[HttpResponseTestCase] @@ -49,27 +47,55 @@ class ClientTest[ case Left(value) => value.statusCode case Right(value) => value.statusCode } - val statusAssert = responseTestCase.map(_.code).getOrElse(httpEp.code) == responseStatusCode + val expectedStatusCode = responseTestCase.map(_.code).getOrElse(httpEp.code) + // val statusAssert = expectedStatusCode == responseStatusCode val outputFromDocument = Document.Decoder.fromSchema(endpoint.output) val expectedOutput = outputFromDocument.decode(responseTestCase.flatMap(_.params).getOrElse(Document.obj())).toOption - println(statusAssert + responseStatusCode.toString) - println(expectedOutput) - responseTestCase.forall(_ => expectedOutput == response.toOption.flatMap(_.body)) && statusAssert + // responseTestCase.forall(_ => expectedOutput == response.toOption.flatMap(_.body)) && statusAssert + ComplianceResponse( + expectedCode = expectedStatusCode, + receivedCode = responseStatusCode, + expectedBody = expectedOutput, + receivedBody = response.toOption.flatMap(_.body), + expectedError = responseTestCase match { + case Some(value) => value.body.getOrElse("") + case None => "" + }, + receivedError = response match { + case Left(value) => Json.parse(value.error).toString() + case Right(value) => "" + } + ) } - def tests(): Seq[Boolean] = + case class ComplianceResponse[O]( + expectedCode: Int, + receivedCode: Int, + expectedBody: Option[O], + receivedBody: Option[O], + expectedError: String, + receivedError: String + ) + + def tests(suite: Option[String] = None) = service.endpoints.flatMap { endpoint => val requests = endpoint.hints .get(HttpRequestTests) .map(_.value) .getOrElse(Nil) + .filter(tc => + suite.isEmpty && tc.documentation.isEmpty || tc.documentation.getOrElse("") == suite.getOrElse("") + ) val responses = endpoint.hints .get(HttpResponseTests) .map(_.value) .getOrElse(Nil) + .filter(tc => + suite.isEmpty && tc.documentation.isEmpty || tc.documentation.getOrElse("") == suite.getOrElse("") + ) val ids = requests.map(_.id).toSet ++ responses.map(_.id).toSet ids diff --git a/smithy4playTest/app/controller/TestController.scala b/smithy4playTest/app/controller/TestController.scala index b5dc8cc7..7b52588b 100644 --- a/smithy4playTest/app/controller/TestController.scala +++ b/smithy4playTest/app/controller/TestController.scala @@ -20,6 +20,7 @@ class TestController @Inject() (implicit EitherT.rightT[Future, ContextRouteError](SimpleTestResponse(Some("TestWithSimpleResponse"))) } + override def testWithOutput( pathParam: String, testQuery: String, diff --git a/smithy4playTest/test/TestControllerTest.scala b/smithy4playTest/test/TestControllerTest.scala index 2a40b4bd..5e43675d 100644 --- a/smithy4playTest/test/TestControllerTest.scala +++ b/smithy4playTest/test/TestControllerTest.scala @@ -1,4 +1,5 @@ -import de.innfactory.smithy4play.client.{ RequestClient, SmithyClientResponse } +import de.innfactory.smithy4play.{ ClientResponse, ContextRoute } +import de.innfactory.smithy4play.client.{ GenericAPIClient, RequestClient, SmithyClientResponse, SmithyPlayClient } import de.innfactory.smithy4play.client.SmithyPlayTestUtils._ import de.innfactory.smithy4play.compliancetests.ClientTest import org.scalatestplus.play.{ BaseOneAppPerSuite, FakeApplicationFactory, PlaySpec } @@ -9,8 +10,9 @@ 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.TestRequestBody -import smithy4s.ByteArray +import smithy4s.internals.InputOutput +import testDefinitions.test.{ TestControllerService, TestControllerServiceGen, TestRequestBody } +import smithy4s.{ ByteArray, Endpoint, GenLift, HintMask, Hints, Monadic, Service, ShapeId, Transformation } import java.io.File import java.nio.file.Files @@ -57,13 +59,28 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli "controller.TestController" must { "new autoTest test" in { - new ClientTest(testControllerClient).tests().map(_ mustBe true) + new ClientTest(testControllerClient).tests().map { result => + result.expectedCode mustBe result.receivedCode + result.expectedBody mustBe result.receivedBody + } + } + + "autoTest 500" in { + new ClientTest(testControllerClient).tests(Some("500")).map { result => + result.expectedCode must not be result.receivedCode + println(result.receivedError) + println(result.expectedError) + result.receivedError mustBe result.expectedError + } } "route to Test Endpoint" in { val result = testControllerClient.test().awaitRight - + val testX: TestControllerServiceGen[GenLift[ClientResponse]#λ] = GenericAPIClient( + TestControllerServiceGen + ) + println(testX) result.statusCode mustBe result.expectedStatusCode } diff --git a/smithy4playTest/testSpecs/HttpTest.smithy b/smithy4playTest/testSpecs/200TestSuite.smithy similarity index 100% rename from smithy4playTest/testSpecs/HttpTest.smithy rename to smithy4playTest/testSpecs/200TestSuite.smithy diff --git a/smithy4playTest/testSpecs/500TestSuite.smithy b/smithy4playTest/testSpecs/500TestSuite.smithy new file mode 100644 index 00000000..8533ae61 --- /dev/null +++ b/smithy4playTest/testSpecs/500TestSuite.smithy @@ -0,0 +1,29 @@ +$version: "2" + +namespace testDefinitions.test + +use smithy4s.api#simpleRestJson +use smithy.test#httpRequestTests +use smithy.test#httpResponseTests + + +apply TestThatReturnsError @httpRequestTests([ + { + id: "test3", + method: "GET", + uri: "/error", + protocol: simpleRestJson, + documentation: "500", + code: 200 + } +]) + +apply TestThatReturnsError @httpResponseTests([ + { + id: "test3", + protocol: simpleRestJson, + body: "{\"message\":\"this is supposed to fail\"}", + documentation: "500", + code: 200 + } +]) \ No newline at end of file diff --git a/smithy4playTest/testSpecs/TestController.smithy b/smithy4playTest/testSpecs/TestController.smithy index 96e9941c..28f02f16 100644 --- a/smithy4playTest/testSpecs/TestController.smithy +++ b/smithy4playTest/testSpecs/TestController.smithy @@ -58,6 +58,17 @@ structure BlobResponse { operation Health { } + +@httpResponseTests([ + { + id: "test2", + protocol: simpleRestJson, + params: { + message: "TestWithSimpleResponse" + }, + code: 200 + } +]) @readonly @http(method: "GET", uri: "/", code: 200) operation Test { From bb17eadb00c61c5a23b16c17c170087965605e2b Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 18 Oct 2022 12:58:07 +0200 Subject: [PATCH 3/7] remove old TestControllerClient --- ...lientTest.scala => ComplianceClient.scala} | 2 +- .../test/TestControllerClient.scala | 52 ------------------- smithy4playTest/test/TestControllerTest.scala | 34 +++++------- .../testSpecs/TestController.smithy | 11 ---- 4 files changed, 15 insertions(+), 84 deletions(-) rename smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/{ClientTest.scala => ComplianceClient.scala} (99%) delete mode 100644 smithy4playTest/test/TestControllerClient.scala diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ComplianceClient.scala similarity index 99% rename from smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala rename to smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ComplianceClient.scala index f89c5a74..edb618a2 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ClientTest.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/compliancetests/ComplianceClient.scala @@ -10,7 +10,7 @@ import smithy4s.{ Document, Endpoint, GenLift, Monadic, Service } import scala.concurrent.duration.DurationInt import scala.concurrent.{ Await, ExecutionContext } -class ClientTest[ +class ComplianceClient[ Alg[_[_, _, _, _, _]], Op[_, _, _, _, _] ]( diff --git a/smithy4playTest/test/TestControllerClient.scala b/smithy4playTest/test/TestControllerClient.scala deleted file mode 100644 index cfb0bdf9..00000000 --- a/smithy4playTest/test/TestControllerClient.scala +++ /dev/null @@ -1,52 +0,0 @@ -import de.innfactory.smithy4play.ClientResponse -import de.innfactory.smithy4play.client.{ RequestClient, SmithyPlayClient } -import smithy4s.ByteArray -import testDefinitions.test.{ - BlobRequest, - BlobResponse, - QueryRequest, - SimpleTestResponse, - TestControllerService, - TestControllerServiceGen, - TestRequestBody, - TestRequestWithQueryAndPathParams, - TestWithOutputResponse -} - -import scala.concurrent.ExecutionContext - -class TestControllerClient(additionalHeaders: Map[String, Seq[String]] = Map.empty, baseUri: String = "/")(implicit - ec: ExecutionContext, - client: RequestClient -) extends TestControllerService[ClientResponse] { - - val smithyPlayClient = new SmithyPlayClient(baseUri, TestControllerService.service) - - override def test(): ClientResponse[SimpleTestResponse] = - smithyPlayClient.send(TestControllerServiceGen.Test(), Some(additionalHeaders)) - - override def testWithOutput( - pathParam: String, - testQuery: String, - testHeader: String, - body: TestRequestBody - ): ClientResponse[TestWithOutputResponse] = smithyPlayClient.send( - TestControllerServiceGen.TestWithOutput(TestRequestWithQueryAndPathParams(pathParam, testQuery, testHeader, body)), - Some(additionalHeaders ++ Map("Content-Type" -> Seq("application/json"))) - ) - - override def health(): ClientResponse[Unit] = - smithyPlayClient.send(TestControllerServiceGen.Health(), Some(additionalHeaders)) - - override def testWithBlob(body: ByteArray, contentType: String): ClientResponse[BlobResponse] = - smithyPlayClient.send( - TestControllerServiceGen.TestWithBlob(BlobRequest(body, contentType)), - Some(additionalHeaders) - ) - - override def testWithQuery(testQuery: String): ClientResponse[Unit] = - smithyPlayClient.send(TestControllerServiceGen.TestWithQuery(QueryRequest(testQuery)), Some(additionalHeaders)) - - override def testThatReturnsError(): ClientResponse[Unit] = - smithyPlayClient.send(TestControllerServiceGen.TestThatReturnsError(), Some(additionalHeaders)) -} diff --git a/smithy4playTest/test/TestControllerTest.scala b/smithy4playTest/test/TestControllerTest.scala index 5e43675d..1d982239 100644 --- a/smithy4playTest/test/TestControllerTest.scala +++ b/smithy4playTest/test/TestControllerTest.scala @@ -1,7 +1,6 @@ -import de.innfactory.smithy4play.{ ClientResponse, ContextRoute } -import de.innfactory.smithy4play.client.{ GenericAPIClient, RequestClient, SmithyClientResponse, SmithyPlayClient } +import de.innfactory.smithy4play.client.{ GenericAPIClient, RequestClient, SmithyClientResponse } import de.innfactory.smithy4play.client.SmithyPlayTestUtils._ -import de.innfactory.smithy4play.compliancetests.ClientTest +import de.innfactory.smithy4play.compliancetests.ComplianceClient import org.scalatestplus.play.{ BaseOneAppPerSuite, FakeApplicationFactory, PlaySpec } import play.api.Application import play.api.Play.materializer @@ -10,9 +9,8 @@ import play.api.libs.json.{ Json, OWrites } import play.api.mvc.{ AnyContentAsEmpty, Result } import play.api.test.FakeRequest import play.api.test.Helpers._ -import smithy4s.internals.InputOutput -import testDefinitions.test.{ TestControllerService, TestControllerServiceGen, TestRequestBody } -import smithy4s.{ ByteArray, Endpoint, GenLift, HintMask, Hints, Monadic, Service, ShapeId, Transformation } +import testDefinitions.test.{ TestControllerServiceGen, TestRequestBody } +import smithy4s.ByteArray import java.io.File import java.nio.file.Files @@ -51,7 +49,9 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli } } - val testControllerClient = new TestControllerClient() + val genericClient = GenericAPIClient( + TestControllerServiceGen + ) override def fakeApplication(): Application = new GuiceApplicationBuilder().build() @@ -59,28 +59,22 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli "controller.TestController" must { "new autoTest test" in { - new ClientTest(testControllerClient).tests().map { result => + new ComplianceClient(genericClient).tests().map { result => result.expectedCode mustBe result.receivedCode result.expectedBody mustBe result.receivedBody } } "autoTest 500" in { - new ClientTest(testControllerClient).tests(Some("500")).map { result => + new ComplianceClient(genericClient).tests(Some("500")).map { result => result.expectedCode must not be result.receivedCode - println(result.receivedError) - println(result.expectedError) result.receivedError mustBe result.expectedError } } "route to Test Endpoint" in { - val result = testControllerClient.test().awaitRight - val testX: TestControllerServiceGen[GenLift[ClientResponse]#λ] = GenericAPIClient( - TestControllerServiceGen - ) - println(testX) + val result = genericClient.test().awaitRight result.statusCode mustBe result.expectedStatusCode } @@ -89,7 +83,7 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli val testQuery = "thisIsATestQuery" val testHeader = "thisIsATestHeader" val body = TestRequestBody("thisIsARequestBody") - val result = testControllerClient.testWithOutput(pathParam, testQuery, testHeader, body).awaitRight + val result = genericClient.testWithOutput(pathParam, testQuery, testHeader, body).awaitRight val responseBody = result.body.get result.statusCode mustBe result.expectedStatusCode @@ -129,13 +123,13 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli } "route to Health Endpoint" in { - val result = testControllerClient.health().awaitRight + val result = genericClient.health().awaitRight result.statusCode mustBe result.expectedStatusCode } "route to error Endpoint" in { - val result = testControllerClient.testThatReturnsError().awaitLeft + val result = genericClient.testThatReturnsError().awaitLeft result.toErrorResponse.message must include("fail") result.statusCode mustBe 500 @@ -145,7 +139,7 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli val path = getClass.getResource("/testPicture.png").getPath val file = new File(path) val pngAsBytes = ByteArray(Files.readAllBytes(file.toPath)) - val result = testControllerClient.testWithBlob(pngAsBytes, "image/png").awaitRight + val result = genericClient.testWithBlob(pngAsBytes, "image/png").awaitRight result.statusCode mustBe result.expectedStatusCode pngAsBytes mustBe result.body.get.body diff --git a/smithy4playTest/testSpecs/TestController.smithy b/smithy4playTest/testSpecs/TestController.smithy index 28f02f16..96e9941c 100644 --- a/smithy4playTest/testSpecs/TestController.smithy +++ b/smithy4playTest/testSpecs/TestController.smithy @@ -58,17 +58,6 @@ structure BlobResponse { operation Health { } - -@httpResponseTests([ - { - id: "test2", - protocol: simpleRestJson, - params: { - message: "TestWithSimpleResponse" - }, - code: 200 - } -]) @readonly @http(method: "GET", uri: "/", code: 200) operation Test { From 7c2b75171872cae8982661c9538b6115c9de9904 Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 18 Oct 2022 13:11:35 +0200 Subject: [PATCH 4/7] scala formatting --- .../de/innfactory/smithy4play/client/GenericAPIClient.scala | 2 +- smithy4playTest/app/controller/TestController.scala | 1 - smithy4playTest/test/TestControllerTest.scala | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala index fbebc3d3..77e1314c 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala @@ -9,7 +9,7 @@ class GenericAPIClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]]( service: Service[Alg, Op] )(implicit ec: ExecutionContext, client: RequestClient) { - val smithyPlayClient = new SmithyPlayClient("/", service) + val smithyPlayClient = new SmithyPlayClient("/", service) def transformer(additionalHeaders: Option[Map[String, Seq[String]]]): Alg[GenLift[ClientResponse]#λ] = service.transform(this.opToResponse(additionalHeaders)) diff --git a/smithy4playTest/app/controller/TestController.scala b/smithy4playTest/app/controller/TestController.scala index 7b52588b..b5dc8cc7 100644 --- a/smithy4playTest/app/controller/TestController.scala +++ b/smithy4playTest/app/controller/TestController.scala @@ -20,7 +20,6 @@ class TestController @Inject() (implicit EitherT.rightT[Future, ContextRouteError](SimpleTestResponse(Some("TestWithSimpleResponse"))) } - override def testWithOutput( pathParam: String, testQuery: String, diff --git a/smithy4playTest/test/TestControllerTest.scala b/smithy4playTest/test/TestControllerTest.scala index 1d982239..e3d6a408 100644 --- a/smithy4playTest/test/TestControllerTest.scala +++ b/smithy4playTest/test/TestControllerTest.scala @@ -73,7 +73,6 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli } "route to Test Endpoint" in { - val result = genericClient.test().awaitRight result.statusCode mustBe result.expectedStatusCode } From d3ac00f4326fcb40fce94825da2c068bbcac4501 Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 18 Oct 2022 14:09:19 +0200 Subject: [PATCH 5/7] refactor: removing client from implicit parameters --- .../smithy4play/client/GenericAPIClient.scala | 36 ++++++++++++------- .../smithy4play/client/SmithyPlayClient.scala | 7 ++-- .../client/SmithyPlayClientEndpoint.scala | 5 +-- .../de/innfactory/smithy4play/package.scala | 2 ++ smithy4playTest/test/TestControllerTest.scala | 9 +++-- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala index 77e1314c..cceec985 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/GenericAPIClient.scala @@ -1,30 +1,42 @@ package de.innfactory.smithy4play.client -import de.innfactory.smithy4play.ClientResponse -import smithy4s.{ GenLift, Service, Transformation } +import de.innfactory.smithy4play.{ ClientRequest, ClientResponse } +import smithy4s.{ Service, Transformation } import scala.concurrent.ExecutionContext -class GenericAPIClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]]( - service: Service[Alg, Op] -)(implicit ec: ExecutionContext, client: RequestClient) { +private class GenericAPIClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]]( + service: Service[Alg, Op], + client: RequestClient +)(implicit ec: ExecutionContext) { - val smithyPlayClient = new SmithyPlayClient("/", service) - def transformer(additionalHeaders: Option[Map[String, Seq[String]]]): Alg[GenLift[ClientResponse]#λ] = + private val smithyPlayClient = new SmithyPlayClient("/", service, client) + + /* Takes a service and creates a Transformation[Op, ClientRequest] */ + private def transformer(additionalHeaders: Option[Map[String, Seq[String]]]): Alg[ClientRequest] = service.transform(this.opToResponse(additionalHeaders)) - def opToResponse(additionalHeaders: Option[Map[String, Seq[String]]]): Transformation[Op, GenLift[ClientResponse]#λ] = - new Transformation[Op, GenLift[ClientResponse]#λ] { + /* uses the SmithyPlayClient to transform a Operation to a ClientResponse */ + 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) } } object GenericAPIClient { + + implicit class EnhancedGenericAPIClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](service: Service[Alg, Op]) { + def withClient( + client: RequestClient, + additionalHeaders: Option[Map[String, Seq[String]]] = None + )(implicit ec: ExecutionContext) = apply(service, additionalHeaders, client) + } def apply[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]]( serviceI: Service[Alg, Op], - additionalHeaders: Option[Map[String, Seq[String]]] = None - )(implicit ec: ExecutionContext, client: RequestClient): Alg[GenLift[ClientResponse]#λ] = - new GenericAPIClient(serviceI).transformer(additionalHeaders) + additionalHeaders: Option[Map[String, Seq[String]]] = None, + client: RequestClient + )(implicit ec: ExecutionContext): Alg[ClientRequest] = + new GenericAPIClient(serviceI, client).transformer(additionalHeaders) } diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClient.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClient.scala index 920603db..64752881 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClient.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClient.scala @@ -7,8 +7,9 @@ import scala.concurrent.ExecutionContext class SmithyPlayClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _], F[_]]( baseUri: String, - service: smithy4s.Service[Alg, Op] -)(implicit executionContext: ExecutionContext, client: RequestClient) { + service: smithy4s.Service[Alg, Op], + client: RequestClient +)(implicit executionContext: ExecutionContext) { def send[I, E, O, SI, SO]( op: Op[I, E, O, SI, SO], @@ -19,7 +20,7 @@ class SmithyPlayClient[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _], F[_]]( HttpEndpoint .cast(endpoint) .map(httpEndpoint => - new SmithyPlayClientEndpoint(endpoint, baseUri, additionalHeaders, httpEndpoint, input).send() + new SmithyPlayClientEndpoint(endpoint, baseUri, additionalHeaders, httpEndpoint, input, client).send() ) .get } diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClientEndpoint.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClientEndpoint.scala index 7a1346f0..367974c1 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClientEndpoint.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/client/SmithyPlayClientEndpoint.scala @@ -14,8 +14,9 @@ private[smithy4play] class SmithyPlayClientEndpoint[Op[_, _, _, _, _], I, E, O, baseUri: String, additionalHeaders: Option[Map[String, Seq[String]]], httpEndpoint: HttpEndpoint[I], - input: I -)(implicit executionContext: ExecutionContext, client: RequestClient) { + input: I, + client: RequestClient +)(implicit executionContext: ExecutionContext) { private val codecs: codecs = smithy4s.http.json.codecs(smithy4s.api.SimpleRestJson.protocol.hintMask ++ HintMask(InputOutput)) diff --git a/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala b/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala index 629a0887..11edca0d 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/package.scala @@ -23,6 +23,8 @@ package object smithy4play { 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] diff --git a/smithy4playTest/test/TestControllerTest.scala b/smithy4playTest/test/TestControllerTest.scala index e3d6a408..7530d88e 100644 --- a/smithy4playTest/test/TestControllerTest.scala +++ b/smithy4playTest/test/TestControllerTest.scala @@ -1,3 +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.SmithyPlayTestUtils._ import de.innfactory.smithy4play.compliancetests.ComplianceClient @@ -9,7 +11,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.{ TestControllerServiceGen, TestRequestBody } +import testDefinitions.test.{ SimpleTestResponse, TestControllerServiceGen, TestRequestBody } import smithy4s.ByteArray import java.io.File @@ -45,13 +47,10 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli headersWithContentType = if (contentType.isDefined) headers + ("Content-Type" -> Seq(contentType.get)) else headers } yield SmithyClientResponse(bodyConsumed, headersWithContentType, result.header.status) - } } - val genericClient = GenericAPIClient( - TestControllerServiceGen - ) + val genericClient = TestControllerServiceGen.withClient(FakeRequestClient) override def fakeApplication(): Application = new GuiceApplicationBuilder().build() From d0c7150ccfab27d5c6b757c93459099d36d3073f Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 18 Oct 2022 14:13:15 +0200 Subject: [PATCH 6/7] refactor: remove unused dependencies --- project/Dependencies.scala | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 36c7a144..bad2e309 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -11,15 +11,11 @@ object Dependencies { val smithyCore = "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion val smithyJson = "com.disneystreaming.smithy4s" %% "smithy4s-json" % smithy4sVersion - val classgraph = "io.github.classgraph" % "classgraph" % "4.8.149" + val classgraph = "io.github.classgraph" % "classgraph" % "4.8.149" val smithyVersion = "1.24.0" - val model = "software.amazon.smithy" % "smithy-model" % smithyVersion val testTraits = "software.amazon.smithy" % "smithy-protocol-test-traits" % smithyVersion - val build = "software.amazon.smithy" % "smithy-build" % smithyVersion - val awsTraits = - "software.amazon.smithy" % "smithy-aws-traits" % smithyVersion val scalatestPlus = "org.scalatestplus.play" %% "scalatestplus-play" % "5.1.0" % Test @@ -28,10 +24,7 @@ object Dependencies { lazy val list = Seq( smithyCore, smithyJson, - model, testTraits, - build, - awsTraits, classgraph, scalatestPlus, typesafePlay, From f33dd22cf231edff1d31d3aea3938db115af9fb2 Mon Sep 17 00:00:00 2001 From: Moritz Lintterer Date: Tue, 18 Oct 2022 15:16:21 +0200 Subject: [PATCH 7/7] adding basic bearer auth validation --- .../smithy4play/SmithyPlayEndpoint.scala | 19 ++++++++++++++++-- .../app/controller/TestController.scala | 4 ++++ smithy4playTest/test/TestControllerTest.scala | 6 ++++++ .../testSpecs/TestController.smithy | 20 ++++++++++++++----- 4 files changed, 42 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 ca686573..c47d2c02 100644 --- a/smithy4play/src/main/scala/de/innfactory/smithy4play/SmithyPlayEndpoint.scala +++ b/smithy4play/src/main/scala/de/innfactory/smithy4play/SmithyPlayEndpoint.scala @@ -1,7 +1,7 @@ package de.innfactory.smithy4play import akka.util.ByteString -import cats.data.EitherT +import cats.data.{ EitherT, Validated } import play.api.mvc.{ AbstractController, ControllerComponents, @@ -13,10 +13,11 @@ import play.api.mvc.{ Results } import smithy4s.{ ByteArray, Endpoint, Interpreter } -import smithy4s.http.{ CodecAPI, HttpEndpoint, Metadata, PathParams } +import smithy4s.http.{ CaseInsensitive, CodecAPI, HttpEndpoint, Metadata, PathParams } import smithy4s.schema.Schema import cats.implicits._ import play.api.libs.json.Json +import smithy.api.{ Auth, HttpBearerAuth } import scala.concurrent.{ ExecutionContext, Future } @@ -51,6 +52,13 @@ class SmithyPlayEndpoint[F[_] <: ContextRoute[_], Op[ pathParams <- getPathParams(v1, httpEp) metadata = getMetadata(pathParams, v1) input <- getInput(request, metadata) + _ <- EitherT( + Future( + Validated + .cond(validateAuthHints(metadata), (), Smithy4PlayError("Unauthorized", 401)) + .toEither + ) + ) res <- impl(endpoint.wrap(input)) .run( RoutingContext @@ -68,6 +76,13 @@ class SmithyPlayEndpoint[F[_] <: ContextRoute[_], Op[ } .getOrElse(Action(NotFound("404"))) + private def validateAuthHints(metadata: Metadata) = { + for { + authSet <- endpoint.hints.get(Auth.tag) + _ <- authSet.value.find(_.value == HttpBearerAuth.id.show) + } yield metadata.headers.contains(CaseInsensitive("Authorization")) + }.getOrElse(true) + private def getPathParams( v1: RequestHeader, httpEp: HttpEndpoint[I] diff --git a/smithy4playTest/app/controller/TestController.scala b/smithy4playTest/app/controller/TestController.scala index b5dc8cc7..fe493235 100644 --- a/smithy4playTest/app/controller/TestController.scala +++ b/smithy4playTest/app/controller/TestController.scala @@ -57,4 +57,8 @@ class TestController @Inject() (implicit override def statusCode: Int = 500 }) } + + override def testAuth(): ContextRoute[Unit] = Kleisli { rc => + EitherT.rightT[Future, ContextRouteError](()) + } } diff --git a/smithy4playTest/test/TestControllerTest.scala b/smithy4playTest/test/TestControllerTest.scala index 7530d88e..3807d6e0 100644 --- a/smithy4playTest/test/TestControllerTest.scala +++ b/smithy4playTest/test/TestControllerTest.scala @@ -142,5 +142,11 @@ class TestControllerTest extends PlaySpec with BaseOneAppPerSuite with FakeAppli result.statusCode mustBe result.expectedStatusCode pngAsBytes mustBe result.body.get.body } + + "route to Auth Test" in { + val result = genericClient.testAuth().awaitLeft + + result.statusCode mustBe 401 + } } } diff --git a/smithy4playTest/testSpecs/TestController.smithy b/smithy4playTest/testSpecs/TestController.smithy index 96e9941c..c0667781 100644 --- a/smithy4playTest/testSpecs/TestController.smithy +++ b/smithy4playTest/testSpecs/TestController.smithy @@ -3,25 +3,25 @@ namespace testDefinitions.test use smithy4s.api#simpleRestJson - +@httpBearerAuth @simpleRestJson service TestControllerService { version: "0.0.1", - operations: [Test, TestWithOutput, Health, TestWithBlob, TestWithQuery, TestThatReturnsError] + operations: [Test, TestWithOutput, Health, TestWithBlob, TestWithQuery, TestThatReturnsError, TestAuth] } - +@auth([]) @http(method: "POST", uri: "/blob", code: 200) operation TestWithBlob { input: BlobRequest, output: BlobResponse } - +@auth([]) @readonly @http(method: "GET", uri: "/error", code: 200) operation TestThatReturnsError { } - +@auth([]) @readonly @http(method: "GET", uri: "/query", code: 200) operation TestWithQuery { @@ -53,17 +53,20 @@ structure BlobResponse { contentType: String } +@auth([]) @readonly @http(method: "GET", uri: "/health", code: 200) operation Health { } +@auth([]) @readonly @http(method: "GET", uri: "/", code: 200) operation Test { output: SimpleTestResponse } +@auth([]) @http(method: "POST", uri: "/test/{pathParam}", code: 200) operation TestWithOutput { input: TestRequestWithQueryAndPathParams, @@ -112,3 +115,10 @@ structure TestResponseBody { } +@auth([httpBearerAuth]) +@http(method: "GET", uri: "/auth", code: 200) +operation TestAuth { + +} + +