how to make this play scala controller work? - scala

I am trying to create a controller that extracts a file on the fly from an archive and then then renders it as a static asset with the default Asset controller
import play.api.Play.current
// import controllers.Assets
import play.api.Logger
import myextract.Extract
import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.concurrent.Execution.Implicits.defaultContext
object MyAssets extends Controller {
// drop the version and serve the asset
def at(path: String, file: String): Action[AnyContent] = Action.async {
Logger.info("looking for file = %s" format file)
val ret = Future {
Extract.getFile(path, file) // extracts file from an archive
// throws exception if file cannot be extracted
}
ret.map {
v => Assets.at(path, file)
}
}
}
I get these errors at compile
./activator compile
[info] Loading project definition from /home/sylvain/yo-scala/project
[info] Set current project to play-slick-advanced (in build file:/home /sylvain/yo-scala/)
[info] Compiling 1 Scala source and 1 Java source to /home/sylvain/yo-scala/target/scala-2.11/classes...
[error] /home/sylvain/yo-scala/app/controllers/MyAssets.scala:14: overloaded method value async with alternatives:
[error] (block: play.api.mvc.Request[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error] (block: => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent]
[error] cannot be applied to (scala.concurrent.Future[play.api.mvc.Action[play.api.mvc.AnyContent]])
[error] def at(path: String, file: String): Action[AnyContent] = Action.async {
[error]
[error] one error found
[error] (compile:compile) Compilation failed

You either have to make sure your action returns a Future:
import play.api.Play.current
// import controllers.Assets
import play.api.Logger
import myextract.Extract
import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.concurrent.Execution.Implicits.defaultContext
object MyAssets extends Controller {
// drop the version and serve the asset
def at(path: String, file: String): Action[AnyContent] = Action.async {
Logger.info("looking for file = %s" format file)
val ret = Future {
Extract.getFile(path, file) // extracts file from an archive
// throws exception if file cannot be extracted
}
ret.map {
v => scala.concurrent.Future {Assets.at(path, file)}
}
}
}
Or change it so it's not async anymore:
import play.api.Play.current
// import controllers.Assets
import play.api.Logger
import myextract.Extract
import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.concurrent.Execution.Implicits.defaultContext
object MyAssets extends Controller {
// drop the version and serve the asset
def at(path: String, file: String): Action[AnyContent] = Action {
Logger.info("looking for file = %s" format file)
val ret = Future {
Extract.getFile(path, file) // extracts file from an archive
// throws exception if file cannot be extracted
}
ret.map {
v => Assets.at(path, file)
}
}
}

Related

type mismatch with akka.http.scaladsl.server.Route

I've created a http server with akka http as follows:
import akka.actor.typed.{ActorRef, ActorSystem}
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Route
import com.sweetsoft.LoggerActor.Log
import akka.actor.typed.scaladsl.adapter._
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.model._
import com.sweetsoft._
import akka.http.scaladsl.server.Directives._
import akka.stream.typed.scaladsl.ActorMaterializer
import scala.concurrent.Future
object ProducerActor {
private val route: Option[ActorRef[ProducerMessage]] => Option[ActorRef[Log]] => Route
= store => logger =>
path("producer") {
post {
entity(as[ProducerMessage]) { msg =>
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
}
}
}
def create[A](store: Option[ActorRef[ProducerMessage]], logger: Option[ActorRef[Log]])
(implicit system: ActorSystem[A])
: Future[ServerBinding] = {
implicit val materializer = ActorMaterializer()
//Please log
Http()(system.toUntyped).bindAndHandle(route(store)(logger), getServerIp, getServerPort)
}
}
The compiler complains:
[error] /home/developer/scala/plugger/src/main/scala/com/sweetsoft/producer/ProducerActor.scala:35:56: type mismatch;
[error] found : akka.http.scaladsl.server.Route
[error] (which expands to) akka.http.scaladsl.server.RequestContext => scala.concurrent.Future[akka.http.scaladsl.server.RouteResult]
[error] required: akka.stream.scaladsl.Flow[akka.http.scaladsl.model.HttpRequest,akka.http.scaladsl.model.HttpResponse,Any]
[error] Http()(system.toUntyped).bindAndHandle(route(store)(logger), getServerIp, getServerPort)
Do I forget to import any libraries?
From the documentation:
Using Route.handlerFlow or Route.asyncHandler a Route can be lifted into a handler Flow or async handler function to be used with a bindAndHandleXXX call from the Core Server API.
Note: There is also an implicit conversion from Route to Flow[HttpRequest, HttpResponse, Unit] defined in the RouteResult companion, which relies on Route.handlerFlow.
Therefore, you have at least three options:
Call Route.handlerFlow:
...bindAndHandle(Route.handlerFlow(route(store)(logger)), ...)
Import the methods in the Route companion object and do the same as above, except now you can drop the explicit reference to the Route object:
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Route._
...bindAndHandle(handlerFlow(route(store)(logger)), ...)
Import akka.http.scaladsl.server.RouteResult._:
import akka.http.scaladsl.server.RouteResult._
...bindAndHandle(route(store)(logger), ...)

missing parameter type on file upload with silhouette

now implement file-uploading with silhouette.
got from the original
https://github.com/playframework/play-scala-fileupload-example/tree/2.5.x
silhouette
https://github.com/mohiva/play-silhouette-seed/tree/4.0.0/app
i want to fix def upload method with silhouette to indicate identity(user name) in fileUpload2.scala.html , so added following line;
silhouette.SecuredAction.async { implicit request =>
Future.successful(Ok(views.html.fileUpload2(request.identity,fileOption)))
}
refered to this link;
http://grokbase.com/p/gg/play-framework/133pt061br/2-1-0-scala-action-question
but not work well.
(FileUploadController.scala)
package controllers
import java.io.File
import java.nio.file.attribute.PosixFilePermission._
import java.nio.file.attribute.PosixFilePermissions
import java.nio.file.{ Files, Path }
import java.util
import javax.inject._
import com.mohiva.play.silhouette.api._
import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository
import com.mohiva.play.silhouette.api.services.AvatarService
import com.mohiva.play.silhouette.api.util.PasswordHasherRegistry
import com.mohiva.play.silhouette.impl.providers._
import com.mohiva.play.silhouette.api.Authenticator.Implicits._
import com.mohiva.play.silhouette.api.util.{ Clock, Credentials }
import com.mohiva.play.silhouette.api.actions.SecuredRequest
import utils.auth.{ DefaultEnv, WithProvider }
import forms.{ FileUploadForm, FileUploadFormSupport }
import models.User
import models.services.{ AuthTokenService, UserService }
import net.ceedubs.ficus.Ficus._
import akka.stream.IOResult
import akka.stream.scaladsl._
import akka.util.ByteString
import play.api._
import play.api.data.Form
import play.api.data.Forms._
import play.api.i18n.{ I18nSupport, Messages, MessagesApi }
import play.api.libs.streams._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.mailer.{ Email, MailerClient }
import play.api.mvc.MultipartFormData.FilePart
import play.api.mvc._
import play.core.parsers.Multipart.FileInfo
import scala.concurrent.{ ExecutionContext, Future }
import scala.concurrent.duration._
import scala.language.postfixOps
class FileUploadController #Inject() (
val messagesApi: MessagesApi,
silhouette: Silhouette[DefaultEnv],
userService: UserService,
authInfoRepository: AuthInfoRepository,
authTokenService: AuthTokenService,
avatarService: AvatarService,
passwordHasherRegistry: PasswordHasherRegistry,
mailerClient: MailerClient,
implicit val webJarAssets: WebJarAssets,
socialProviderRegistry: SocialProviderRegistry,
ec: ExecutionContext,
credentialsProvider: CredentialsProvider
)
extends Controller with i18n.I18nSupport {
private val logger = org.slf4j.LoggerFactory.getLogger(this.getClass)
def fileuploadview = silhouette.SecuredAction.async { implicit request =>
Future.successful(Ok(views.html.fileUpload(request.identity, FileUploadForm.form)))
}
type FilePartHandler[A] = FileInfo => Accumulator[ByteString, FilePart[A]]
def handleFilePartAsFile: FilePartHandler[File] = {
case FileInfo(partName, filename, contentType) =>
val attr = PosixFilePermissions.asFileAttribute(util.EnumSet.of(OWNER_READ, OWNER_WRITE))
val path: Path = Files.createTempFile("multipartBody", "tempFile", attr)
val file = path.toFile
val fileSink: Sink[ByteString, Future[IOResult]] = FileIO.toPath(path)
val accumulator: Accumulator[ByteString, IOResult] = Accumulator(fileSink)
accumulator.map {
case IOResult(count, status) =>
logger.info(s"count = $count, status = $status")
FilePart(partName, filename, contentType, file)
}
}
def operateOnTempFile(file: File) = {
val size = Files.size(file.toPath)
logger.info(s"size = ${size}")
Files.deleteIfExists(file.toPath)
size
}
def upload = Action(parse.multipartFormData(handleFilePartAsFile)) { implicit request =>
val fileOption = request.body.file("picName").map {
case FilePart(key, filename, contentType, file) =>
logger.info(s"key = ${key}, filename = ${filename}, contentType = ${contentType}, file = $file")
val data = operateOnTempFile(file)
data
}
FileUploadFormSupport.picsave(_)
silhouette.SecuredAction.async { implicit request =>
Future.successful(Ok(views.html.fileUpload2(request.identity,fileOption)))
}
}
}
the problem is the last lines;
silhouette.SecuredAction.async { implicit request =>
Future.successful(Ok(views.html.fileUpload2(request.identity,fileOption)))
compile error;
missing parameter type
occured.
You use the Silhouette action at the wrong place. You should write:
def upload = silhouette.SecuredAction.async(parse.multipartFormData(handleFilePartAsFile)) {
implicit request =>
// Your upload code
}
A Silhouette action is a default Play action with the advantage that the request type is extended with the identity and the authenticator.

Scala - making a WS http request

UPDATED: Method is returning type ANY rather than type Future[string]. Require return of type String.
I'm making a http request using the play.ws library 2.6. This was previously done with a curl request but this only uses basic authentication.
Below is my code and I'm trying to return a json string from this function to be deserialised in another method.
import java.io.{File, InputStream}
import java.nio.file.Paths
import javax.inject._
import org.apache.commons.io.FileUtils
import play.api._
import play.api.http.HttpEntity
import play.api.libs.ws._
import play.api.mvc._
import play.api.Play.current
import scala.collection.mutable.ListBuffer
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import sys.process._
#Singleton
class BuildService #Inject() (
ws: WSClient,
ec: ExecutionContext,
config: Configuration) {
def bbApiRequest(requestUrl: String, timeout: FiniteDuration):
Future[String] = {
val request = ws
.url(requestUrl)
.withAuth(
"user",
"pw1234",
WSAuthScheme.BASIC)
.get()
Await.result(request, timeout)
val returner = request.map(_.json)
} // <-- line 72 in below error points here.
}
When run it produces the error:
[error] C:\my_path\app\services\BuildService.scala:72: type mismatch;
[error] found : Unit
[error] required: scala.concurrent.Future[String]
[error] }
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[info] Compiling 1 Scala source to C:\my_path\restapi\target\scala-
2.12\classes...
[error] C:\my_path\restapi\app\services\BuildService.scala:72: type
mismatch;
[error] found : Unit
[error] required: scala.concurrent.Future[String]
[error] }
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
I'm trying to get ideally:
A string return (case class and json method to unpack a string)
Synchronous request (if async I'll need to wait to complete to progress application)
Secure (allows use of tokens for verification)
Appreciate any help!
Here is the function I use:
// first work with Futures the Play Controller will support that!
def bbApiRequest(requestUrl: String): Future[String] = {
// call the webservice with basic authentication
wsClient.url(requestUrl)
.withAuth("tester", "pwd123", WSAuthScheme.BASIC)
.get()
.map(checkStatus) // function that evaluates the HTTP Status
.map(_.json) // get the json
.map(Json.prettyPrint) // get it as string
}
I would create a case class directly like:
.map(jsValue => handleJson(jsValue.validate[YourModel])) // or create a model of it (instead) instead of .map(Json.prettyPrint)
Edit
Here an example of checkStatus:
protected def checkStatus(resp: WSResponse): WSResponse = {
resp.status match {
case Status.OK => resp
case Status.NOT_FOUND => throw WebNotFoundException()
case Status.FORBIDDEN | Status.UNAUTHORIZED => throw WebAccessForbiddenException()
case Status.NOT_ACCEPTABLE => throw WebNotAcceptableException()
case _ => throw WebBadStatusException(resp.status + " - " + resp.statusText.toString)
}
}
The Exception are created by myself.
The authorization can also be inputs into the function or retrieved from environment variables within the method (much easier to manage).
Simply needed to use .body on the Await call, which converts the output to generic type string.
package utils
import javax.inject._
import play.api._
import play.api.http.HttpEntity
import play.api.libs.ws._
import play.api.mvc._
import play.api.Play.current
import scala.collection.mutable.ListBuffer
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import sys.process._
#Singleton
class HTTPRequest #Inject() (
ws: WSClient,
ec: ExecutionContext,
config: Configuration) {
def bbApiRequest(requestUrl: String, timeout: FiniteDuration) = {
val request = ws
.url(requestUrl)
.withAuth(
"user",
"PW123",
WSAuthScheme.BASIC)
.get()
Await.result(request, timeout).body
}
}

could not find implicit value for parameter env: com.mohiva.play.silhouette.api.Environment[utils.auth.DefaultEnv]

I'm using a Silhouette v4.0 library with play framework 2.5.
And have been trying to write test code using play specs2.
But, I get the following error with my test class as below.
Error Message
[error] could not find implicit value for parameter env: com.mohiva.play.silhouette.api.Environment[utils.auth.DefaultEnv]
.withAuthenticator[DefaultEnv](identity.loginInfo)
^
Here's the test class
package controllers
import com.google.inject.AbstractModule
import org.joda.time.DateTime
import org.specs2.specification.Scope
import org.specs2.matcher._
import org.specs2.mock._
import play.api.test._
import play.api.libs.json._
import play.api.libs.json.Json
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.mailer.{ MailerClient, Email }
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.inject.bind
import com.mohiva.play.silhouette.test._
import com.mohiva.play.silhouette.api._
import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository
import com.mohiva.play.silhouette.api.util._
import com.mohiva.play.silhouette.impl.providers._
import net.codingwell.scalaguice.ScalaModule
import utils.auth.DefaultEnv
class TestControllerSpec extends PlaySpecification with Mockito {
"case" in new Context {
new WithApplication(application) {
val request = FakeRequest(POST, "/api/test")
.withAuthenticator[DefaultEnv](identity.loginInfo) // <-
val result = route(app, request).get
status(result) must be equalTo OK
}
}
trait Context extends Scope {
val identity = User(
loginInfo = LoginInfo(..)
..
)
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
class FakeModule extends AbstractModule with ScalaModule {
def configure() = {
bind[Environment[DefaultEnv]].toInstance(env)
}
}
lazy val application = new GuiceApplicationBuilder()
.overrides(new FakeModule)
.build
}
}
There are some other test classes similar to this class are properly able to compile and execute.
It's kind of implicit problem with scope..
Therefore, I tried to import all the same as another test class which's able to compile properly. But, still unable to compile.
Missing some import?
As the compiler states, you're missing an implicit value. Use the following, which is modeled after one of Silhouette's specs:
class TestControllerSpec extends PlaySpecification with Mockito {
"the POST request" should {
"return an OK response" in new Context {
new WithApplication(application) {
val identity = User(LoginInfo(...))
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest(POST, "/api/test")
.withAuthenticator(identity.loginInfo)
val result = route(app, request).get
status(result) must be equalTo OK
}
}
}
trait Context extends Scope {
...
}
}

Error testing DAL in slick scala

Pretty new to scala and play and I have been assigned a task to test someone else app,which is running fine btw.Please check if my tests are right and what is the error.
This is employeeEntry.scala file in models
package models
import models.database.Employee
import play.api.db.slick.Config.driver.simple._
import play.api.db.slick._
import play.api.Play.current
case class EmployeeEntry(eid :Int, ename: String, eadd: String, emob: String)
object Employee {
val DBemp = TableQuery[Employee]
def savedat(value: EmployeeEntry):Long = {
DB.withSession { implicit session =>
DBemp+=EmployeeEntry(eid=value.eid,ename=value.ename,eadd=value.eadd,emob=value.emob)
}}
/*val query = for (c <- Employee) yield c.ename
val result = DB.withSession {
implicit session =>
query.list // <- takes session implicitly
}*/
//val query = for (c <- Employee) yield c.ename
def getPersonList: List[EmployeeEntry] = DB.withSession { implicit session => DBemp.list }
def Update: Int = DB.withSession { implicit session =>
(DBemp filter (_.eid === 1) map (s => (s.ename,s.eadd))) update ("test","khair")}
def delet :Int =DB.withSession {
implicit session => (DBemp filter (_.eid === 1)).delete
}
}
And this is file Employee.scala in models/database
package models.database
import models._
import models.EmployeeEntry
import play.api.db.slick.Config.driver.simple._
import scala.slick.lifted._
class Employee(tag:Tag) extends Table[EmployeeEntry](tag,"employee")
{
//val a = "hello"
def eid = column[Int]("eid", O.PrimaryKey)
def ename = column[String]("name", O.DBType("VARCHAR(50)"))
def emob = column[String]("emob",O.DBType("VARCHAR(10)"))
def eadd =column[String]("eadd",O.DBType("VARCHAR(100)"))
def * = (eid, ename, emob, eadd) <> (EmployeeEntry.tupled, EmployeeEntry.unapply)
}
Finally this is my test I am running,which is failing :
import play.api.db.slick.Config.driver.simple._
import play.api.db.slick._
import play.api.Play.current
import org.scalatest.FunSpec
import org.scalatest.matchers.ShouldMatchers
import models.database.Employee
import scala.slick.lifted._
import models._
import models.EmployeeEntry
//import scala.slick.driver.H2Driver.simple._
class databasetest extends FunSpec with ShouldMatchers{
describe("this is to check database layer"){
it("can save a row"){
val a = EmployeeEntry(1006,"udit","schd","90909090")
Employee.savedat(a) should be (1)
}
it("getpersonlist"){
Employee.getPersonList.size should be (1)
}
}
}
The test is failing and error is
java.lang.RuntimeException: There is no started application
at scala.sys.package$.error(package.scala:27)
at play.api.Play$$anonfun$current$1.apply(Play.scala:71)
at play.api.Play$$anonfun$current$1.apply(Play.scala:71)
at scala.Option.getOrElse(Option.scala:120)
at play.api.Play$.current(Play.scala:71)
at models.Employee$.getPersonList(EmployeeEntry.scala:27)
at databasetest$$anonfun$1$$anonfun$apply$mcV$sp$2.apply$mcV$sp(databasetest.scala:39)
at databasetest$$anonfun$1$$anonfun$apply$mcV$sp$2.apply(databasetest.scala:39)
at databasetest$$anonfun$1$$anonfun$apply$mcV$sp$2.apply(databasetest.scala:39)
at org.scalatest.Transformer$$anonfun$apply$1.apply$mcV$sp(Transformer.scala:22)
By default play provides spec2 testing framework.so no need to add scalatest framework for unit testing.For database access layer testing required a connection with database so start a fake application.(this is not running code, just an idea to write unit test)
for more detail take a look play doc : https://www.playframework.com/documentation/2.3.x/ScalaFunctionalTestingWithSpecs2
import org.specs2.mutable.Specification
import models.database.Employee
import models._
import models.EmployeeEntry
import play.api.test.FakeApplication
import play.api.test.Helpers.running
import play.api.Play.current
class databasetest extends Specification {
"database layer" should {
"save a row" in {
running(FakeApplication()) {
val a = EmployeeEntry(1006,"udit","schd","90909090")
Employee.savedat(a) must be equalTo (1)
}
}
"get list" in {
running(FakeApplication()) {
Employee.getPersonList.size must be equalTo (1)
}
}
}