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

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?

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

Function with Future return type always returns None

I have the following functions:
//retrieves record from database
def getAll: Future[List[User]] = {
try {
Logger.info("Getting all user record.")
db.run(userTableQuery.to[List].result)
}
catch {
case ex: Exception => Logger.error("Exception in getting all user record. " + ex)
Future {
List[User]()
}
}
}
//checks whethe the email exist in list or not
def checkEmail(email : String): Future[Option[User]] ={
/* userRepo.getAll.map(userList => userList.filter(user => user.email == email).map { value => println(value)
userList
})*/
userRepo.getAll.map(userList => userList.filter(user => user.email == email).headOption)
}
//allows to sign in and redirects to dashboard
def signIn() = {
Logger.debug("signingIn in progress. ")
loginForm.bindFromRequest.fold(
formWithErrors => {
Logger.error("Sign-In badRequest.")
Future(BadRequest(views.html.home(webJarAssets, formWithErrors, signUpForm)))
},
validData => {
userService.checkEmail(validData.email).map(value => {
value match {
case Some(us) =>Redirect(routes.HomeController.homePage).flashing("ERROR" -> "User exist")
case None => Redirect(routes.HomeController.homePage).flashing("ERROR" -> "User doesn't exist")
}
}
)
}
)
}
But when I call signin() it always returns None.
I used some debugger code and I guess filter() inside checkMail() is not working properly.
However getall() working properly and gives all the record in database.
I think the problem is in how you compare the user email with the provided one inside the filter in the checkMail() function.
Strings equality is a bit tricky, if you compare them using == you are comparing the objects and not the values so you should use .equals() to compare the values.
You can read more on this blog post
Try to rewrite checkMail() like this:
def checkEmail(email : String): Future[Option[User]] ={
userRepo.getAll.map(userList => userList.filter(user => user.email.equals( email ) ).headOption)
}
You can also simplify .filter() e .headOption using find(), it does the same thing only in one command. You can rewrite it like this:
def checkEmail(email : String): Future[Option[User]] ={
userRepo.getAll.map(userList => userList.find(user => user.email.equals( email ) ) )
}
Instead of using filter you may use find method under checkmail. And also, since this is scala you are using "==" correctly, please see blog here
I hope this code would fix:
//checks whethe the email exist in list or not
def checkEmail(email : String): Future[Option[User]] ={
/* userRepo.getAll.map(userList => userList.filter(user => user.email == email).map { value => println(value)
userList
})*/
userRepo.getAll.map(userList => userList.find(user => user.email == email))
}
I tried to simulate/experiment regarding your implementation in direct way using the terminal:
scala> case class User(email: String)
defined class User
scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
scala> val allUser = scala.concurrent.Future {List(User("carl#test.com"), User("los#test.com"), User("pos#test.com"))}
allUser: scala.concurrent.Future[List[User]] = scala.concurrent.impl.Promise$DefaultPromise#751d3241
scala> val checkmail = allUser.map(userlist=>userlist.find(user=>user.email == "carl#test.com"))
checkmail: scala.concurrent.Future[Option[User]] = scala.concurrent.impl.Promise$DefaultPromise#1358b28e
scala> val rslt = checkmail.map(value => value match {case Some(x) =>println(x); x.email case None => println("None"); "nothing" })
rslt: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise#18324f97
User(carl#test.com)
scala> import scala.concurrent.duration._
import scala.concurrent.duration._
scala> import scala.concurrent._
import scala.concurrent._
scala> Await.result(rslt, 3 seconds)
warning: there was one feature warning; re-run with -feature for details
res8: String = carl#test.com

ReactiveMongo Scala DAO pattern

I have the following DAO, Service Object and Controller pattern in my Playframework project:
CredentialsDAO.scala:
def find(authType: AuthType.Value, authAccountId: String) =
collection.find(
Json.obj(Fields.AuthType → authType,
Fields.AuthAccountId → authAccountId)).one[Credentials].recover(wrapLastError)
CredentialsService.scala:
def checkEmailPassword(email: String, password: String) = credentialsDAO.find(AuthType.EmailPassword, email).map(_.get) //Needs review
In my controller:
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 {
validCredential <- credentialsManager.checkEmailPassword(auth.email, auth.password)
validPassword <- BCrypt.checkFuture(auth.password, validCredential.passwordHash)
validAccount <- accountManager.getAccount(validCredential)
session <- sessionManager.createSession(validAccount.id, validAccount.roles)
touchedSession <- sessionManager.touchSession(session.id)
} yield AuthResponse(session.id, session.token, validAccount.id, validAccount.roles)
authRequestResult map {
case res: AuthResponse => Ok(Json.toJson(res))
case _ => NotFound
}
})
}
}
Question: Is this 'pattern' okay from a functional programming perspective? In particular, I'm bothered by this line in CredentialsService.scala:
def checkEmailPassword(email: String, password: String) = credentialsDAO.find(AuthType.EmailPassword, email).map(_.get) //Needs review
I'm new to Scala but I think there are better ways of dealing with _.get in the line above? Any suggestions/ideas would be highly appreciated.
Thanks in advance.

Scala convert Future[Option[Account]] to Future[Either[String, Option[Account]]]

I have the following methods that return futures:
db.find(uuid, accountName): Future[Option[Account]]
db.add(uuid, account): Future[Option[Account]]
I want to write a program that finds a user in the database using a future.
If not, a user entry should be added to the database.
In either case, I want to get back a Future[Either[String, Option[Account]]] object.
I am getting a compiler error when I write the following:
def add(uuid: UUID, account: Account): Future[Either[String, Option[Account]]] =
db.find(userId, account.name).map {
case None =>
db.add(userId.uuid, account).map(Right(_))
case Some(existAccount) =>
Left(s"Account already exist $existAccount")
}
Edit:
This code can`t compile with error:
Error:(77, 41) type mismatch;
found : scala.concurrent.Future[scala.util.Right[Nothing,Option[Account]]]
required: Either[String,Option[Account]]
db.add(userId, account).map(Right(_))
^
Resolved:
Solution is:
object Test extends App {
import concurrent._
import ExecutionContext.Implicits._
import duration.Duration._
type Account = String
val found = """(.+)\1""".r
def find(name: String) = name match {
case found(_*) => Future(Option(name))
case _ => Future(None)
}
def add(account: Account) = Future(Option(account * 2))
def f(account: Account): Future[Either[String, Option[Account]]] = {
find(account) flatMap {
case None => add(account) map (Right(_))
case Some(x) => Future successful Left(s"already $account")
}
}
Console println Await.result(f("bob"), Inf)
Console println Await.result(f("bobbob"), Inf)
}

Scala Type Mismatch

I am having a problem with type mismatch.
type mismatch; found : Option[models.User] required: models.User
def authenticate = Action { implicit request =>
signinForm.bindFromRequest.fold(
formWithErrors => BadRequest(html.signin(formWithErrors)),
user => Redirect(routes.Application.active).withSession(Security.username -> User.getUserName(user))
)
}
How can I force the function to accept Option[models.User] or can I convert the models.User into an Option?
The error occurs here: User.getUserName(user). getUserName requires models.User types.
===============================================
Update with all code used:
From User.scala
def authenticate(email: String, password: String) : Option[User] = {
(findByEmail(email)).filter { (user => BCrypt.checkpw(password, user.password)) }
}
def findByEmail(email: String) : Option[User] = {
UserDAO.findOne(MongoDBObject("email" -> email))
}
From Application.scala
val signinForm = Form {
mapping(
"email" -> nonEmptyText,
"password" -> text)(User.authenticate)(_.map(user => (user.email, "")))
.verifying("Invalid email or password", result => result.isDefined)
}
def authenticate = Action { implicit request =>
signinForm.bindFromRequest.fold(
formWithErrors => BadRequest(html.signin(formWithErrors)),
user => Redirect(routes.Application.active).withSession(Security.username -> User.getUserName(user.get))
)
}
To de-option an Option[User] into a User, you can do one of the following:
1) The unsafe way. Only do this if you are sure that optUser is not None.
val optUser: Option[User] = ...
val user: User = optUser.get
2) The safe way
val optUser: Option[User] = ...
optUser match {
case Some(user) => // do something with user
case None => // do something to handle the absent user
}
3) The monadic safe way
val optUser: Option[User] = ...
optUser.map(user => doSomething(user))
The biggest thing is that, if it's possible that optUser might actually be None, you need to figure out what you actually want to happen in the case that there is no User object.
There's a lot more information about Option in other StackOverflow questions if you'd like to read more.