I am not understanding something about this example in Play 2.7 documentation
class ExampleSpec extends PlaySpec with GuiceOneServerPerSuite {
// Override app if you need an Application with other than
// default parameters.
override def fakeApplication(): Application = {
GuiceApplicationBuilder()
.appRoutes(app => {
case ("GET", "/") => app.injector.instanceOf(classOf[DefaultActionBuilder]) { Ok("ok") }
}).build()
}
"test server logic" in {
val wsClient = app.injector.instanceOf[WSClient]
val myPublicAddress = s"localhost:$port"
val testPaymentGatewayURL = s"http://$myPublicAddress"
// The test payment gateway requires a callback to this server before it returns a result...
val callbackURL = s"http://$myPublicAddress/callback"
// await is from play.api.test.FutureAwaits
val response = await(wsClient.url(testPaymentGatewayURL).addQueryStringParameters("callbackURL" -> callbackURL).get())
response.status mustBe OK
}
}
The problem is this code:
.appRoutes(app => {
case ("GET", "/") => app.injector.instanceOf(classOf[DefaultActionBuilder]) { Ok("ok") }
I get message that it expecting Application => PartialFunction[(String, String), Handler]
What is Handler? Is my controller?
This is down to lack of type inference I assume.
If you add the required type annotation (i.e. add : PartialFunction[(String, String), Handler]) you should be able to compile:
override def fakeApplication(): Application = {
GuiceApplicationBuilder()
.appRoutes(app => {
case ("GET", "/") => app.injector.instanceOf(classOf[DefaultActionBuilder]) { x => play.api.mvc.Results.Forbidden }
}: PartialFunction[(String, String), Handler]
).build()
}
Related
I've been trying to use websocket on play framework 2.5 in scala.
Now, I want to exchange multiple message types.
But, I get the following errors when a client sends a json via websocket.
Do I have to choose different way to exchange multiple message types?
missing something or is there any easier way?
[error] a.a.OneForOneStrategy - {"sort":"post", "to":"receiver","message":"Test"} (of class play.api.libs.json.JsObject)
scala.MatchError: {"sort":"post", "to":"receiver","message":"Test"} (of class play.api.libs.json.JsObject)
at controllers.MessageController$MessageActor$$anonfun$receive$1.applyOrElse(MessageController.scala:72)
at akka.actor.Actor$class.aroundReceive(Actor.scala:514)
at controllers.MessageController$MessageActor.aroundReceive(MessageController.scala:63)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:527)
at akka.actor.ActorCell.invoke(ActorCell.scala:496)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257)
at akka.dispatch.Mailbox.run(Mailbox.scala:224)
at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
models.scala
abstract class WSMessageIn()
// Classes for input
case class PostMessage(
sort: String = "post",
to: String,
...
message: Option[String] = None
) extends WSMessageIn
object PostMessage {
implicit val postMessageFormat = Json.format[PostMessage]
}
case class MessageFromIn(
sort: String = "receive",
from: String,
...
message: Option[String] = None
) extends WSMessageIn
object MessageFromIn {
implicit val messageFromInFormat = Json.format[MessageFromIn]
}
object WSMessageIn {
implicit def json2object(value: JsValue): WSMessageIn = {
(value \ "sort").as[String] match {
case "post" => value.as[PostMessage]
case "receive" => value.as[MessageFromIn]
}
}
implicit def object2json(in: WSMessageIn): JsValue = {
in match {
case in: PostMessage => Json.toJson(in)
case in: MessageFromIn => Json.toJson(in)
}
}
}
MessageController.scala
class MessageController #Inject() (
silhouette: Silhouette[DefaultEnv],
..
implicit val system: ActorSystem,
implicit val materializer: Materializer)
extends Controller {
class MessageActor(out: ActorRef) extends Actor {
override def receive = {
case message: JsValue =>
message match { // !!The error occurs here
case message: PostMessage =>
...
case message: MessageFromIn =>
...
}
}
}
object MessageActor {
def props(out: ActorRef) = Props(new MessageActor(out))
}
implicit val messageFlowTransformer = MessageFlowTransformer.jsonMessageFlowTransformer[JsValue, JsValue]
def socket() = WebSocket.acceptOrResult[JsValue, JsValue] { request =>
implicit val req = Request(request, AnyContentAsEmpty)
silhouette.SecuredRequestHandler { securedRequest =>
Future.successful(HandlerResult(Ok, Some(securedRequest.identity)))
}.map {
case HandlerResult(r, Some(user)) => Right(ActorFlow.actorRef(out => MessageActor.props(out)))
case HandlerResult(r, None) => Left(r)
}
}
}
As is: WebSocket.acceptOrResult[JsValue, JsValue] you expect your websocket to receive JsValue.
This seems to match you code below in MessageActor:
override def receive = {
case message: JsValue => // ...
}
However, you shouldn't expect the conversion from JsValue to WSMessageIn to happen without your intervention.
What you could do is simply:
override def receive = {
case message: JsValue =>
WSMessageIn.json2object(message) match {
case message: PostMessage =>
...
case message: MessageFromIn =>
...
}
}
You were probably expecting an implicitly conversion, you can get it by giving a hint to the compiler (i.e., saying what you expect):
override def receive = {
case message: JsValue =>
(message: WSMessageIn) match {
case message: PostMessage =>
...
case message: MessageFromIn =>
...
}
}
Note: you could turn abstract class WSMessageIn() into: sealed trait WSMessageIn
Looking to create functionality to upload a file using multipart/form-data. However I cannot grasp how to change the MultipartFormData and store it in the file system. Below is what I have so far.
trait Service extends HttpService {
private final lazy val fileWorker = actorRefFactory.actorOf(Props[FileServicesActor])
implicit def executionContext = actorRefFactory.dispatcher
val demoRoute: Route = {
path("file") {
post {
respondWithMediaType(`application/json`) {
entity(as[MultipartFormData]) { formData =>
uploadFile(formData)
}
}
}
}
}
private def uploadFile(data: MultipartFormData)= {
val response = (fileWorker ? UploadFile(data)).mapTo[Any].map { case t: Success => t
case e: Error => Error.outputError(e)
case _ => Failure(_)
}.recover { case e: Exception => Failure(e)
}
complete(response)
}
}
The function resolves to this
def uploadFile(data: MultipartFormData) = {
val file = data.get("file")
//not sure what to do with data here...
}
I'm updating my application from Specs2 2.3.12 to 3.6.1 and am having trouble updating the class which wraps our unit tests in a transaction.
The 2.3.12 class:
class DbTx extends AroundOutside[Session] {
var session: Option[Session] = None
def around[T : AsResult](t: => T) = {
Db.withTransaction { implicit s =>
session = Some(s)
val result = AsResult(t)
s.rollback()
result
}
}
def outside: Session = session.get
}
its usage:
"my unit test" in (new DbTx).apply { implicit session: Session =>
...
}
What I've tried in 3.6.1
class DbTx extends ForEach[Session] {
var session: Option[Session] = None
def foreach[T : AsResult](t: Session => T) = {
Db.withTransaction { implicit s =>
session = Some(s)
val result = AsResult(t)
s.rollback()
result
}
}
}
its usage:
"my unit test" in (new DbTx).foreach { implicit session: Session =>
...
}
but this seemed to produce an infinite loop between lines 6 & 4 of that block.
I also tried
class DbTx extends Around {
def around[T: AsResult](t: => T): Result = {
super.around {
Db.withTransaction { implicit s: Session =>
val result = AsResult(t)
s.rollback()
result
}
}
}
}
its usage:
"my unit test" in (new DbTx).around { implicit session: Session =>
...
}
but that results in
could not find implicit value for evidence parameter of type AsResult[Session => MatchResult[ ... ]]
I also tried
class DbTx extends Fixture[Session] {
def apply[T: AsResult](t: Session => T): Result = {
Db.withTransaction { implicit s: Session =>
val result = AsResult(t)
s.rollback()
result
}
}
}
its usage:
"my unit test" in (new DbTx) { implicit session: Session =>
...
}
which results in
could not find implicit value for evidence parameter of type AsResult[Session => T]
Edit
I'm also getting an infinite loop with this code:
import org.specs2.execute.AsResult
import org.specs2.mutable.Specification
import org.specs2.specification.ForEach
class DbTxSpec extends Specification with ForEach[Session] {
def foreach[T: AsResult](t: Session => T) = {
Db.withTransaction { implicit s => // infinite loop between here
try AsResult(t) // and here
finally s.rollback()
}
}
"my unit test" in { implicit session: Session =>
true must beTrue
}
}
If you want to pass in a Session you need to use have your specification extend the ForEach trait, not a special object. Something like:
class DbTxSpec extends Specification with ForEach[Session] {
var session: Option[Session] = None
def foreach[T : AsResult](t: Session => T) = {
Db.withTransaction { implicit s =>
session = Some(s)
try AsResult(t(session))
finally s.rollback()
}
}
"my unit test" in { implicit session: Session =>
...
}
}
I want TestHttp class to be able to receive http requests or messages from other actors. How can I do it?
Code:
object Main extends App with SimpleRoutingApp {
implicit val system = ActorSystem("system")
import system.dispatcher
implicit val timeout = Timeout(240.seconds)
startServer(interface = "localhost", port = 3000) {
get {
path("register" / IntNumber) { n =>
respondWithMediaType(MediaTypes.`application/json`) { ctx =>
val future = IO(Http) ? Bind(system.actorOf(Props[TestHttp]), interface = "localhost", port = 3000 + n)
future onSuccess {
case Http.Bound(msg) => ctx.complete(s"Ok:"+msg)
case _ => ctx.complete("...")
}
}
} // : Route == RequestContext => Unit
} // : Route
}
}
trait TestHttpService extends HttpService {
val oneRoute = {
path("test") {
complete("test")
}
}
}
class TestHttp extends Actor with TestHttpService {
def actorRefFactory = context
val sealedRoute = sealRoute(oneRoute)
def receive = {
// case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => //not working
// sender ! HttpResponse(entity = "PONG")
case ctx: RequestContext => sealedRoute(ctx) //not working
}
// def receive = runRoute(oneRoute) //it works
}
Actor.Receive is a partial function that takes Any value and returns Unit (PartialFunction[Any, Unit]), so you can do it by regular PF composition.
HttpService.runRoute returns Actor.Receive (see https://github.com/spray/spray/blob/master/spray-routing/src/main/scala/spray/routing/HttpService.scala#L31)
So, your solution would be:
class TestHttp extends Actor with TestHttpService {
def actorRefFactory = context
val sealedRoute = sealRoute(oneRoute)
def receive = {
case s: String => println(s"Just got string $s")
} orElse runRoute(oneRoute)
}
I'm trying to craft a ScalaInterceptor that looks for an X-Forwarded-Proto header, so basically if its in production or behind a proxy then Play! auto redirects to SSL.
I've run into issues with getting this code to compile, and I'm also not sure whether this will work with the SecureSocial plugin. There are specific reasons why we aren't setting SSL=true in SecureSocial.conf that I won't go into here.
Here's what I have in my Global.scala
def WithHttpsRedirect[A](action: Action[A]): Action[A] = {
Action(action.parser) { request =>
val result = action(request)
request.headers.get("X-Forwarded-Proto").collect {
case "https" =>
result
case "http" =>
val url = "https://"+request.host+request.uri
Redirect(url)
} getOrElse {
result
}
}
}
override def onRouteRequest(request: RequestHeader): Option[Handler] = {
super.onRouteRequest(request).map { handler =>
handler match {
case a: Action[_] => WithHttpsRedirect(a)
case _ => handler
}
}
}
I'm getting a compiler error after the getOrElse:
[error] found : scala.concurrent.Future[play.api.mvc.SimpleResult]
[error] required: play.api.mvc.Result
[error] result
[error] ^
Your help is greatly appreciated!
Replace:
Action(action.parser) { request =>
with:
Action.async(action.parser) { request =>
You made need to also replace:
Redirect(url)
with:
Future.successful(Redirect(url))
Changed my method of attack, and instead implemented a filter instead of overriding onRouteRequest:
In Global.scala:
object Global extends WithFilters(HttpsFilter) with GlobalSettings
then HttpsFilter.scala:
import play.api.mvc.Results._
import play.api.mvc.{SimpleResult, RequestHeader, Filter}
import scala.concurrent._
import ExecutionContext.Implicits.global
object HttpsFilter extends Filter {
def apply(next: (RequestHeader) => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
request.headers.get("X-Forwarded-Proto").collect {
case "https" =>
next(request)
case "http" =>
val url = "https://"+request.host+request.uri
Future{ Redirect(url) }
} getOrElse {
next(request)
}
}
}