Skip to content

Commit

Permalink
fix router using actions template and add tests (#296)
Browse files Browse the repository at this point in the history
* fix router using actions template and add tests

* fix headers and formatting
  • Loading branch information
drZoid authored Sep 3, 2020
1 parent d85c904 commit 030fcdd
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 4 deletions.
22 changes: 21 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ val playTestdata = Project("play-grpc-testdata", file("play-testdata"))
)
.enablePlugins(build.play.grpc.NoPublish)

val playActionsTestData = Project("play-grpc-actions-testdata", file("play-actions-testdata"))
.dependsOn(playRuntime)
.settings(
scalacOptions += "-Xlint:-unused,_", // can't do anything about unused things in generated code
javacOptions -= "-Xlint:deprecation", // can't do anything about deprecations in generated code
ReflectiveCodeGen.extraGenerators ++= List(
"play.grpc.gen.scaladsl.PlayScalaServerCodeGenerator",
),
ReflectiveCodeGen.codeGeneratorSettings += "use_play_actions",
libraryDependencies ++= Seq(
Dependencies.Compile.play,
Dependencies.Compile.grpcStub,
Dependencies.Compile.playAkkaHttpServer,
Dependencies.Compile.playAkkaHttp2Support,
Dependencies.Compile.akkaDiscovery,
),
)
.pluginTestingSettings
.enablePlugins(build.play.grpc.NoPublish)

val playGenerators = Project("play-grpc-generators", file("play-generators"))
.enablePlugins(SbtTwirl, BuildInfoPlugin)
.settings(
Expand All @@ -95,7 +115,7 @@ val playGenerators = Project("play-grpc-generators", file("play-generators"))
)

val playTestkit = Project("play-grpc-testkit", file("play-testkit"))
.dependsOn(playRuntime, playTestdata % "test")
.dependsOn(playRuntime, playTestdata % "test", playActionsTestData % "test")
.settings(
libraryDependencies ++= Seq(
Dependencies.Compile.play,
Expand Down
19 changes: 19 additions & 0 deletions play-actions-testdata/src/main/proto/helloworld.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// must mirror the scala-interop-test one since that is shown in the docs
syntax = "proto3";

option java_multiple_files = true;
option java_package = "example.myapp.helloworld.grpc.actions";
option java_outer_classname = "HelloWorldProto";

package helloworld;

service GreeterService {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package @service.packageName

import akka.annotation.InternalApi
import akka.actor.ActorSystem
import akka.grpc.GrpcServiceException
import akka.grpc.{GrpcServiceException, Trailers}
import play.grpc.internal.PlayRouterUsingActions
import akka.grpc.scaladsl.GrpcExceptionHandler.defaultMapper
import akka.http.scaladsl.model.Uri.Path
Expand All @@ -23,7 +23,7 @@ import scala.concurrent.ExecutionContext
/**
* Abstract base class for implementing @{serviceName} and using as a play Router
*/
abstract class Abstract@{serviceName}Router(mat: Materializer, system: ActorSystem, parsers: PlayBodyParsers, actionBuilder: ActionBuilder[Request, AnyContent], eHandler: ActorSystem => PartialFunction[Throwable, Status] = defaultMapper) extends PlayRouterUsingActions(mat, @{service.name}.name, parsers, actionBuilder) with @{serviceName} {
abstract class Abstract@{serviceName}Router(mat: Materializer, system: ActorSystem, parsers: PlayBodyParsers, actionBuilder: ActionBuilder[Request, AnyContent], eHandler: ActorSystem => PartialFunction[Throwable, Trailers] = defaultMapper) extends PlayRouterUsingActions(mat, @{service.name}.name, parsers, actionBuilder) with @{serviceName} {

@{
val (streamingInputMethods: Seq[String], unaryInputMethods: Seq[String]) = service.methods.partition(_.inputStreaming) match {
Expand All @@ -37,7 +37,7 @@ import scala.concurrent.ExecutionContext
*/
@@InternalApi
final override protected def createHandler(serviceName: String, mat: Materializer): RequestHeader => EssentialAction = {
val handler = @{serviceName}Handler(this, serviceName, eHandler)(mat, system)
val handler = @{serviceName}Handler(this, serviceName, eHandler)(system)
reqOuter =>
implicit val ec: ExecutionContext = mat.executionContext
Path(reqOuter.path) match {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.grpc.scalatest

import org.scalatest.concurrent.IntegrationPatience
import org.scalatest.concurrent.ScalaFutures
import org.scalatestplus.play.PlaySpec
import org.scalatestplus.play.guice.GuiceOneServerPerTest
import play.api.Application
import play.api.inject.bind
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.libs.ws.WSClient
import play.api.routing.Router
import io.grpc.Status
import example.myapp.helloworld.grpc.actions.helloworld._

import akka.grpc.internal.GrpcProtocolNative

/**
* Test for the Play gRPC ScalaTest APIs
*/
class PlayActionsScalaTestSpec
extends PlaySpec
with GuiceOneServerPerTest
with ServerGrpcClient
with ScalaFutures
with IntegrationPatience {

override def fakeApplication(): Application = {
GuiceApplicationBuilder()
.overrides(bind[Router].to[GreeterServiceWithActionsImpl])
.build()
}

implicit def ws: WSClient = app.injector.instanceOf(classOf[WSClient])

"A Play server bound to a gRPC router using actions" must {
"give a 404 when routing a non-gRPC request" in {
val result = wsUrl("/").get.futureValue
result.status must be(404) // Maybe should be a 426, see #396
}
// this test results in a 500
// "give a 415 error when not using a gRPC content-type" in {
// val result = wsUrl(s"/${GreeterService.name}/FooBar").get.futureValue
// result.status must be(415)
// }
// this test results in a 500 error
// "give a grpc 'unimplemented' error when routing a non-existent gRPC method" in {
// val result = wsUrl(s"/${GreeterService.name}/FooBar")
// .addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
// .get
// .futureValue
// result.status must be(200) // Maybe should be a 426, see #396
// result.header("grpc-status") mustEqual Some(Status.Code.UNIMPLEMENTED.value().toString)
// }
"give a grpc 'invalid argument' error when routing an empty request to a gRPC method" in {
val result = wsUrl(s"/${GreeterService.name}/SayHello")
.addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
.get
.futureValue
result.status must be(200)
result.header("grpc-status") mustEqual Some(Status.Code.INVALID_ARGUMENT.value().toString)
}
"work with a gRPC client" in withGrpcClient[GreeterServiceClient] { client: GreeterServiceClient =>
val reply = client.sayHello(HelloRequest("Alice")).futureValue
reply.message must be("Hello, Alice!")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package example.myapp.helloworld.grpc.actions.helloworld

import javax.inject.Inject
import javax.inject.Singleton
import akka.stream.Materializer
import akka.actor.ActorSystem
import play.api.mvc.PlayBodyParsers
import play.api.mvc.DefaultActionBuilder

import scala.concurrent.Future

@Singleton
class GreeterServiceWithActionsImpl @Inject() (
implicit
mat: Materializer,
actorSystem: ActorSystem,
parsers: PlayBodyParsers,
actionBuilder: DefaultActionBuilder,
) extends AbstractGreeterServiceRouter(
mat,
actorSystem,
parsers,
actionBuilder,
) {

override def sayHello(in: HelloRequest): Future[HelloReply] = {
actorSystem.log.error("Saying hello!")
Future.successful(HelloReply(s"Hello, ${in.name}!"))
}

}

0 comments on commit 030fcdd

Please sign in to comment.