How execute spring repository method in for comprehension with implicit - scala

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 ()
}

Related

Handling errors in scala ZIO

I wanted to handle some exceptions in ZIO using catchAll or catchSome as the below :
object Test extends App {
def run(args: List[String]) =
myApp.fold(_ => 1, _ => 0)
val myApp =
for {
_ <- putStrLn(unsafeRun(toINT("3")).toString)
} yield ()
def toINT(s: String): IO[IOException, Int]= {
IO.succeed(s.toInt).map(v => v).catchAll(er =>IO.fail(er))
}
the code succeeded in case I passed a valid format number but it's unable to handle the exception in case I passed invalid format and idea ??
s.toInt gets evaluated outside of the IO monad. What happens is that you evaluate s.toInt first and try to pass the result of that to IO.succeed, but an exception has already been thrown before you can pass anything to IO.succeed. The name of succeed already basically says that you are sure that whatever you pass it is a plain value that cannot fail.
The docs suggest using Task.effect, IO.effect, or ZIO.effect for lifting an effect that can fail into ZIO.
Here is a program that worked for me:
val program =
for {
int <- toINT("3xyz")
_ <- putStrLn(int.toString)
} yield ()
def toINT(s: String): Task[Int] = {
ZIO.fromTry(Try(s.toInt))
}
rt.unsafeRun(program.catchAll(t => putStrLn(t.getMessage)))

Slick for-comprehensions: How to handle an enumerator that can be None or Some-thing?

I have a use-case maybe not specific to Slick but to for-comprehensions. The high level pattern is like this but it leads to a compiler error in enum3:
(for {
enum1 <- // ...
enum2 <- // ...
enum3 <- optionArg.fold(empty result).map { ... }
} yield ())
Then I end up doing the following that compiles but has code duplication namely of enum1 and enum2:
optionArg match {
case (Some(arg)) => {
(for {
enum1 <- // ...
enum2 <- // ...
enum3 <- // do something with arg
} yield ())
}
case None => {
(for {
enum1 <- // ...
enum2 <- // ...
} yield ())
}
}
}
In concrete I have that this repetitive variant compiles:
/**
* Returns the inserted `oauth2Info` instance including the params. We first
* look up the `LoginInfo` by the relevant search criteria, fetching its `userId`
* which is then used to persist a `OAuth2Info` and multiple `OAuth2InfoParam`.
*
* #param extLoginInfo The login info for which the auth info should be added.
* #param extOAuth2Info The TOTP info to add containing the params.
* #return the inserted `oauth2Info` instance including the params.
*/
def add(extLoginInfo: ExtLoginInfo, extOAuth2Info: ExtOAuth2Info): Future[ExtOAuth2Info] = {
val insertion = extOAuth2Info.params match {
case Some(params) => {
(for {
userId <- LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
_ <- (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
_ <- DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
} yield ())
}
case None => {
(for {
userId <- LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
_ <- (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
} yield ())
}
}
db.run(insertion.transactionally).map(_ => extOAuth2Info)
}
and this succinct desired variant doesn't:
def add(extLoginInfo: ExtLoginInfo, extOAuth2Info: ExtOAuth2Info): Future[ExtOAuth2Info] = {
val insertion = (for {
userId <- LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
_ <- (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
_ <- extOAuth2Info.params.fold(DBIOAction.seq()) { params =>
DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
}
} yield ()).transactionally
db.run(insertion).map(_ => extOAuth2Info)
}
Gives compiles error:
[play-silhouette-seed] $ compile
[info] Formatting 1 Scala source ProjectRef(uri("file:/home/skywalker/code/play-silhouette-seed/"), "root")(compile) ...
[info] Compiling 1 Scala source to /home/skywalker/code/play-silhouette-seed/target/scala-2.12/classes ...
[error] /home/skywalker/code/play-silhouette-seed/app/models/daos/OAuth2InfoDaoImpl.scala:58:28: type mismatch;
[error] found : slick.dbio.DBIOAction[scala.collection.immutable.Iterable[Int],slick.dbio.NoStream,slick.dbio.Effect.Write]
[error] required: slick.dbio.DBIOAction[Unit,slick.dbio.NoStream,slick.dbio.Effect]
[error] DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed May 28, 2019 10:42:00 AM
This is a problem with fold when the compiler cannot type-infer the proper generic types for the expression. It does not properly widen the type returned from the first expression to the type of the second one. You need to help the compiler a bit.
It also happens in situations like this
Some(123).fold(None)(_ => Some("123"))
// type mismatch; found: Some[String] required: None.type
You can change fold to map().getOrElse() (then the "good type" comes first and get be inferred).
Or you can add type annotations somewhere, such as
private val noAction: slick.dbio.DBIOAction[Iterable[Int],NoStream,Effect.Write]
= DBIOAction.sequence()
theOption.fold(noAction)(params => .... )
Also, if you find yourself duplicating code, this can be mitigated by moving the common expression out. With functional (side-effect-free) code like Slick, this kind of refactoring is quite safe. You can just build a couple of val or def with the Slick actions you need to run and compose them later.
def add(extLoginInfo: ExtLoginInfo, extOAuth2Info: ExtOAuth2Info): Future[ExtOAuth2Info] = {
val getUserId = LoginInfo.filter { loginInfo => loginInfo.providerId === extLoginInfo.providerID && loginInfo.providerKey === extLoginInfo.providerKey }.map(_.userId).result.head
val insertOAuth = for {
userId <- getUserId
_ = (OAuth2Info += OAuth2InfoRow(userId, extOAuth2Info.accessToken, extOAuth2Info.tokenType, extOAuth2Info.expiresIn, extOAuth2Info.refreshToken))
} yield userId
val insertion = extOAuth2Info.params match {
case Some(params) => {
(for {
userId <- insertOAuth
_ <- DBIOAction.sequence(params.map { param => (OAuth2InfoParam += OAuth2InfoParamRow(userId, param._1, param._2)) })
} yield ())
}
case None =>
insertOAuth
}
db.run(insertion.transactionally).map(_ => extOAuth2Info)
}
​

Scala: Chaining futures, returning the first

I have a scenario where I have a Future[Something]: a which when successful needs to trigger a second Future[Unit]: b. I want to chain a and b together but I only care that a succeeds. if b fails I can just log an error and leave it at that:
So far I have:
def updateSomething(something: Something): Future[Something] = {
val eventual: Future[Something] = repository.update(something)
eventual.onSuccess({
case updated =>
repository.audit(updated.id, "Update successful")
.onFailure({
case throwable: Throwable => Logger.error("Audit failed", throwable)
})
Logger.info("Update Complete")
})
eventual
}
But this does not link the lifecyles of the update and the audit together. e.g when I Await.result(service.updateSomething(...), duration) there is no guarantee the repository.audit future has completed.
flatMap is your friend. You can just use a for-comprehension + recover:
for {
a <- executeA()
_ <- executeB(b).recover{case e => println(e); Future.unit } // awaits B to complete
} yield a
Also you can use more friendly form:
execudeA().flatMap(a =>
executeB().recover { case e => println(e); () }.map(_ => a)
)
also, you can just use a val
val = a <- executeA()
a.andThen{ case _ => executeB(b).recover{case e => println(e)} }
a //and return a

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?

How to create future unit?

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)))