How to create future unit? - scala

I have this code:
def updateDocSetting(data: Seq[ModelDocumentSetting])= {
for (a <- data){
DocumentSettingTable
.filter(_.doc_proc_list_id === a.doc_proc_list_id)
.map(doc => (doc.is_mandatory,doc.is_display,doc.is_deleted))
.update(a.is_mandatory,a.is_display,a.is_deleted)
}
}
I have a problem to get the future slick result on my service code
this is my service code
def updateDocSetting(data: List[ModelDocumentSetting]): Future[Unit] = {
db.run(DalDocumentSetting.updateDocSetting(data))
}
error:type mismatch; found : Unit required:
slick.dbio.DBIOAction[Unit,slick.dbio.NoStream,Nothing]

You can convert the sequence of DBIOActions to a single DBIOAction using DBIO.sequence.
Use yield to get the sequence of DBIO actions:
for (a <- data) yield {
DocumentSettingTable
.filter(_.doc_proc_list_id === a.doc_proc_list_id)
.map(doc => (doc.is_mandatory,doc.is_display,doc.is_deleted))
.update(a.is_mandatory,a.is_display,a.is_deleted)
}
And then:
db.run(DBIO.sequence(DalDocumentSetting.updateDocSetting(data)))

Related

How execute spring repository method in for comprehension with implicit

I want to save data from telegram api in for comprehension type with implicit, but have an error
Error:(61, 9) type mismatch;
found : cats.effect.IO[Unit]
required: scala.concurrent.Future[?]
_ <- IO(userRepository.save(User(msg.from.get.id, msg.from.get.username.get)))
The code in TelegramBot example, which use info.mukel.telegrambot4s 3.0.9 library.
onCommand("/hello") { implicit msg =>
for {
_ <- reply(s"Hello ${msg.from.get.firstName}")
_ <- IO(userRepository.save(User(msg.from.get.id, msg.from.get.username.get)))
} yield ()
}
I tried to delete reply and add, this code compiled, but saving (inside IO) didn`t execute
onCommand("/hello") { implicit msg =>
for {
res <- IO(userRepository.save(User(msg.from.get.id, msg.from.get.username.get)))
} yield res
}
Is it possible to resolve this problem?
Try
onCommand("/hello") { implicit msg =>
for {
_ <- IO.fromFuture(IO.pure(reply(s"Hello ${msg.from.get.firstName}")))
_ <- IO(userRepository.save(User(msg.from.get.id, isBot = true, msg.from.get.username.get)))
} yield ()
}

scala returns doesn't conform to required S_

I got the error
found : scala.concurrent.Future[Option[models.ProcessTemplatesModel]]
required: Option[models.ProcessTemplatesModel]
My function is below
def createCopyOfProcessTemplate(processTemplateId: Int): Future[Option[ProcessTemplatesModel]] = {
val action = processTemplates.filter(_.id === processTemplateId).result.map(_.headOption)
val result: Future[Option[ProcessTemplatesModel]] = db.run(action)
result.map { case (result) =>
result match {
case Some(r) => {
var copy = (processTemplates returning processTemplates.map(_.id)) += ProcessTemplatesModel(None, "[Copy of] " + r.title, r.version, r.createdat, r.updatedat, r.deadline, r.status, r.comment, Some(false), r.checkedat, Some(false), r.approvedat, false, r.approveprocess, r.trainingsprocess)
val composedAction = copy.flatMap { id =>
processTemplates.filter(_.id === id).result.headOption
}
db.run(composedAction)
}
}
}
}
what is my problem in this case?
edit:
my controller function looks like this:
def createCopyOfProcessTemplate(processTemplateId: Int) = Action.async {
processTemplateDTO.createCopyOfProcessTemplate(processTemplateId).map { process =>
Ok(Json.toJson(process))
}
}
Is there my failure?
According to the your code - there are the following issues:
You use two db.run which return futures, but inner future will
not complete. For resolving it you should compose futures with
flatMap or for-comprehension.
You use only one partial-function case Some(_) => for pattern matching
and don't handle another value None.
You can use only one db.run and actions composition.
Your code can be like as:
def createCopyOfProcessTemplate(processTemplateId: Int): Future[Option[ProcessTemplatesModel]] = {
val action = processTemplates.filter(...).result.map(_.headOption)
val composedAction = action.flatMap {
case Some(r) =>
val copyAction = (processTemplates returning processTemplates...)
copyAction.flatMap { id =>
processTemplates.filter(_.id === id).result.headOption
}
case _ =>
DBIO.successful(None) // issue #2 has been resolved here
}
db.run(composedAction) // issue #3 has been resolved here
}
We get rid of issue #1 (because we use actions composition).

Nested futures in Play for Scala does not compile

This Action.async method in Play for Scala should execute the second future only if the first future returns 1, that's why they are nested. If the first future returns a result different than 1 then the second future should never be executed. But I'm getting the following compilation error in f2.map. Why is this error and how to fix it?
type mismatch; found :
scala.concurrent.Future[scala.concurrent.Future[play.api.mvc.Result]]
required: play.api.mvc.Result
def index = Action.async { request =>
val f1 = Future {1}
f1.map {
access => if (access==1) {
val f2 = Future {2}
f2.map { // <-- compilation error
result => {
val json = JsObject(Seq(
"acc" -> JsNumber(access),
"ret" -> JsString("0")
))
Ok(json)
}
}
}
else {
val json = JsObject(Seq(
"acc" -> JsNumber(0),
"ret" -> JsString("0")
))
Future.successful(Ok(json))
}
}
}
You're basically there - just flatMap on f1 instead of map since you're creating another future.

Scala: type mismatch; found : scala.concurrent.Future[Option[models.Account]] required: Option[?]

Give then following code:
AccountsDAO.scala:
def find(id: EntityId): Future[Option[Account]] =
collection.find(Json.obj(Fields.Id -> id)).one[Account]
And AccountService.scala:
def getAccount(id: Option[Credentials]) = id.flatMap {
accountId => accountDAO.find(accountId.accountId) //Throws an error
}
The commented line above throws this error:
type mismatch; found : scala.concurrent.Future[Option[models.Account]] required: Option[?]
What am I missing? Why does flattop return an Option[?]. If I change the return type of the getAccount method as:
def getAccount(id: Option[Credentials]): Future[Option[Account]] = id.flatMap {
accountId => accountDAO.find(accountId.accountId) //Still throws an error
}
I get the error below:
type mismatch; found : Option[Nothing] required: scala.concurrent.Future[Option[models.Account]]
What is going on? What am I missing?
Thanks in advance.
Edit : Here's the code in the controller and what I'm trying to do:
def auth = Action.async(parse.json) { request =>
{
val authRequest = request.body.validate[AuthRequest]
authRequest.fold(
errors => Future(BadRequest),
auth => {
//First verify username and password
val authRequestResult = for {
validCredentials <- credentialsManager.checkEmailPassword(auth.email, auth.password)
account:Option[Account] <- accountManager.getAccount(validCredentials)
session:Session <- sessionManager.createSession(account.get.id, account.get.roles)
touchedSession <- sessionManager.TouchSession(session.id)
} yield AuthResponse(session.id, session.token, account.get.id, account.get.roles)
authRequestResult map {
case res: AuthResponse => Ok(Json.toJson(res))
case _ => NotFound
}
})
}
}
The checkEmailPassword method above returns a Future[Option]:
def checkEmailPassword(email: String, password: String) =
for {
credentials <- credentialsDAO.find(AuthType.EmailPassword, email)
validPassword <- BCrypt.checkFuture(password, credentials)
} yield (credentials)
And credentialsDAO.find:
def find(authType: AuthType.Value, authAccountId: String) =
collection.find(
Json.obj(Fields.AuthType → authType,
Fields.AuthAccountId → authAccountId)).one[Credentials].recover(wrapLastError)
So when checkEmailPassword returns an Option[Credentials] object, is it okay to assume the for comprehension in the controller will not execute any further if it returns a None? Then I could just say something like account:Option[Account] <- accountManager.getAccount(validCredentials.get.id). Is there a better way to structure/organize this code? Any patterns that I can follow/use?

scala, play, futures: combining results from multiple futures

I am using:
Scala 2.10
Play 2.1
Currently, I am using the Future class from scala.concurrent._, but I'm open to trying another API.
I am having trouble combining the results of multiple futures into a single List[(String, String)].
The following Controller method successfully returns the results of a single Future to an HTML template:
def test = Action { implicit request =>
queryForm.bindFromRequest.fold(
formWithErrors => Ok("Error!"),
query => {
Async {
getSearchResponse(query, 0).map { response =>
Ok(views.html.form(queryForm,
getAuthors(response.body, List[(String, String)]())))
}
}
})
}
The method getSearchResult(String, Int) performs a web service API call and returns a Future[play.api.libs.ws.Response]. The method getAuthors(String, List[(String, String)]) returns a List[(String, String)] to the HTML template.
Now, I am trying to call getSearchResult(String, Int) in a for loop to get several Response bodies. The following should give an idea of what I'm trying to do, but I get a compile-time error:
def test = Action { implicit request =>
queryForm.bindFromRequest.fold(
formWithErrors => Ok("Error!"),
query => {
Async {
val authors = for (i <- 0 to 100; if i % 10 == 0) yield {
getSearchResponse(query, i)
}.map { response =>
getAuthors(response.body, List[(String, String)]())
}
Ok(views.html.form(queryForm, authors))
}
})
}
type mismatch; found : scala.collection.immutable.IndexedSeq[scala.concurrent.Future[List[(String, String)]]] required: List[(String, String)]
How can I map the responses of several Future objects to a single Result?
Create a Future parametrized by a List or other Collection of the Result type.
From here:
In Play 1 you can do this:
F.Promise<List<WS.HttpResponse>> promises = F.Promise.waitAll(remoteCall1, remoteCall2, remoteCall3);
// where remoteCall1..3 are promises
List<WS.HttpResponse> httpResponses = await(promises); // request gets suspended here
In Play 2 less direct:
val httpResponses = for {
result1 <- remoteCall1
result2 <- remoteCall2
} yield List(result1, result2)