ReactiveMongo query returning None - scala

I am just new to learning Scala and the related technologies.I am coming across the problem where the loadUser should return a record but its coming empty.
I am getting the following error:
java.util.NoSuchElementException: None.get
I appreciate this is not ideal Scala, so feel free to suggest me improvements.
class MongoDataAccess extends Actor {
val message = "Hello message"
override def receive: Receive = {
case data: Payload => {
val user: Future[Option[User]] = MongoDataAccess.loadUser(data.deviceId)
val twillioApiAccess = context.actorOf(Props[TwillioApiAccess], "TwillioApiAccess")
user onComplete {
case Failure(exception) => println(exception)
case p: Try[Option[User]] => p match {
case Failure(exception) => println(exception)
case u: Try[Option[User]] => twillioApiAccess ! Action(data, u.get.get.phoneNumber, message)
}
}
}
case _ => println("received unknown message")
}
}
object MongoDataAccess extends MongoDataApi {
def connect(): Future[DefaultDB] = {
// gets an instance of the driver
val driver = new MongoDriver
val connection = driver.connection(List("192.168.99.100:32768"))
// Gets a reference to the database "sensor"
connection.database("sensor")
}
def props = Props(new MongoDataAccess)
def loadUser(deviceId: UUID): Future[Option[User]] = {
println(s"Loading user from the database with device id: $deviceId")
val query = BSONDocument("deviceId" -> deviceId.toString)
// By default, you get a Future[BSONCollection].
val collection: Future[BSONCollection] = connect().map(_.collection("profile"))
collection flatMap { x => x.find(query).one[User] }
}
}
Thanks

There is no guaranty the find-one (.one[T]) matches at least one document in your DB, so you get an Option[T].
Then it's up to you to consider (or not) that having found no document is a failure (or not); e.g.
val u: Future[User] = x.find(query).one[User].flatMap[User] {
case Some(matchingUser) => Future.successful(matchingUser)
case _ => Future.failed(new MySemanticException("No matching user found"))
}
Using .get on Option is a bad idea anyway.

Related

Comparing the json data types at runtime using Jackson and Scala

I have an incoming JSON data that looks like below:
{"id":"1000","premium":29999,"eventTime":"2021-12-22 00:00:00"}
Now, I have created a class that will accept this record and will check whether the data type of the incoming record is according to the data types defined in the case class. However, when I am calling the method it is always calling the Failure part of the match case.
case class Premium(id: String, premium: Long, eventTime: String)
class Splitter extends ProcessFunction[String, Premium] {
val outputTag = new OutputTag[String]("failed")
def fromJson[T](json: String)(implicit m: Manifest[T]): Either[String, T] = {
Try {
println("inside")
lazy val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
mapper.readValue[T](json)
} match {
case Success(x) => {
Right(x)
}
case Failure(err) => {
Left(json)
}
}
}
override def processElement(i: String, context: ProcessFunction[String, Premium]#Context, collector: Collector[Premium]): Unit = {
fromJson(i) match {
case Right(data) => {
collector.collect(data)
println("Good Records: " + data)
}
case Left(json) => {
context.output(outputTag, json)
println("Bad Records: " + json)
}
}
}
}
Based on the sample record above, it should pass the Success value but no matter what I pass, it always enters the Failure part. What else is missing?
I am using Scala 2.11.12 and I tried examples from this link and this link but no luck.

how should I optimise the scala code and make it more readable [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have this ugly massive piece of code. It does many things, mostly around querying databases. Each query depends on the result of the previous query or on the parameters of the incoming request body. Queries are done asynchronously using Future. The code works but is not readable. How can I restructure it to make it concise? I have thought of using for instead of map and flatMap but due to dependencies on previous queries, I am not able to figure out how to use result of previous Future in new ones and how to handle error paths.
I am using play 2.6 and scala 2.12.2
def oldSignupUser:Action[AnyContent] = silhouette.UserAwareAction.async {
implicit request => {
val body: AnyContent = request.body
val jsonBody: Option[JsValue] = body.asJson
jsonBody match {
case Some(json) => {
val userProfile: Option[UserProfile] = json.asOpt[UserProfile] userProfile match {
case Some(profile) => {
val loginInfo = LoginInfo(CredentialsProvider.ID, profile.externalProfileDetails.email)
val userKeys = UserKeys(utilities.bucketIDFromEmail(profile.externalProfileDetails.email),profile.externalProfileDetails.email,loginInfo,profile.externalProfileDetails.firstName,profile.externalProfileDetails.lastName)
val findUserFuture: Future[Option[User]] = userRepo.findOne(userKeys) // userFuture will eventually contain the result of database query i.e Some(user) or None
findUserFuture.flatMap { (userOption: Option[User]) =>
userOption match {
case Some(user) => {
if(user.profile.internalProfileDetails.get.confirmed) {
Future {
Ok(Json.toJson(JsonResultError(messagesApi("error.duplicateUser")(langs.availables(0)))))
}
} else {
val userKeys = UserKeys(utilities.bucketIDFromEmail(user.profile.externalProfileDetails.email),user.profile.externalProfileDetails.email,loginInfo,user.profile.externalProfileDetails.firstName,user.profile.externalProfileDetails.lastName)
val userToken:UserToken = utilities.createUserToken(user.id,userKeys,UserTokenType.RegistrationConfirmation)
val userTokenSaveFuture:Future[Option[UserToken]] = userTokenRepo.save(userToken)
logger.trace(s"user token save future ${userTokenSaveFuture}")
userTokenSaveFuture.map( (userTokenOption:Option[UserToken])=>{
userTokenOption match {
case Some(userToken) => {
val signupEmailOption:Option[SignupEmail] = createEmailMessageForUserToken(userToken)
signupEmailOption match {
case Some(signupEmail:SignupEmail) =>{
val _:String = mailerService.sendEmail(signupEmail.subject, signupEmail.from,List(user.profile.externalProfileDetails.email),None,Some(signupEmail.html))
Ok(Json.toJson(JsonResultSuccess(messagesApi("success.userSignupConfirmationEmailSent")(langs.availables(0)))))
}
case None =>{
InternalServerError(Json.toJson(JsonResultError("Internal Server Error")))
}
}
}
case None => {
Ok(Json.toJson(JsonResultError("user not added"))) //Todom - this is misleading as user is added but token isn't
}
}
})
}
}
case None => {
val passwordInfo:PasswordInfo = userRepo.hashPassword(profile.externalProfileDetails.password.get)
val bucketId = utilities.bucketIDFromEmail(profile.externalProfileDetails.email)
val newUser:User = User(
utilities.getUniqueID(),//UUID.randomUUID(),
UserProfile(Some(InternalUserProfile(loginInfo,bucketId,false,Some(passwordInfo))),
profile.externalProfileDetails))
val saveUserFuture:Future[Option[User]] = userRepo.save(newUser)
saveUserFuture.flatMap { (userOption:Option[User]) =>{ userOption match {
case Some(user) => {
val initialPortfolio = user.profile.externalProfileDetails.portfolio
val profileAndPortfolio = profile.externalProfileDetails.copy(portfolio = initialPortfolio)
logger.trace(s"saving external profile and portfolio ${profileAndPortfolio}")
val savedProfileAndPortfolioOptionFuture = userProfileAndPortfolioRepo.save(profileAndPortfolio)
savedProfileAndPortfolioOptionFuture.flatMap(profileAndPortfolioOption =>{
profileAndPortfolioOption match {
case Some(profileAndPortfolio) => {
val userKeys = UserKeys(utilities.bucketIDFromEmail(user.profile.externalProfileDetails.email),user.profile.externalProfileDetails.email,loginInfo,user.profile.externalProfileDetails.firstName,user.profile.externalProfileDetails.lastName)
val userToken:UserToken = utilities.createUserToken(user.id,userKeys,UserTokenType.RegistrationConfirmation)
val userTokenSaveFuture:Future[Option[UserToken]] = userTokenRepo.save(userToken)
userTokenSaveFuture.flatMap( (userTokenOption:Option[UserToken])=>{
userTokenOption match {
case Some(userToken) => {
val signupEmailOption = createEmailMessageForUserToken(userToken)
signupEmailOption match {
case Some(signupEmail) =>{
val _:String = mailerService.sendEmail(signupEmail.subject,signupEmail.from,List(user.profile.externalProfileDetails.email),None,Some(signupEmail.html))
Future{Ok(Json.toJson(JsonResultSuccess((messagesApi("success.userSignupConfirmationEmailSent"))(langs.availables(0)))))}
}
case None =>{
Future{InternalServerError(Json.toJson(JsonResultError("Internal Server Error")))}
}
}
}
case None => {
Future{Ok(Json.toJson(JsonResultError("user not added"))) }
}
}
})
}
case None =>{
Future{InternalServerError(Json.toJson(JsonResultError("Internal Server Error")))}
}
}
})
}
case None => {
Future{Ok(Json.toJson(JsonResultError("unable to add user")))}
}
}
}
}
.recover { case x => {
x match {
case _:EmailException =>InternalServerError(Json.toJson(JsonResultError("The server encountered internal error and couldn't sent email to the email id.")))
case _ => InternalServerError(Json.toJson(JsonResultError("Internal Server Error")))
}
}
}
}
}
}
.recover { case x => {
logger.trace("Future failed in signupUser. In recover. Returning Internal Server Error"+x)
x match {
case _:EmailException =>InternalServerError(Json.toJson(JsonResultError("The server encountered internal error and couldn't sent email to the email id.")))
case _ => InternalServerError(Json.toJson(JsonResultError("Internal Server Error")))
}
}
}
}
case None => Future {
logger.trace("invalid profile structure")
Ok(Json.toJson(JsonResultError(messagesApi("error.incorrectBodyStructure")(langs.availables(0))))) }
}
}
case None => Future {
Ok(Json.toJson(JsonResultError(messagesApi("error.incorrectBodyType")(langs.availables(0))))) }
}
}
}
Update - the question was closed before I could solve the problem. Many thanks to mfirry and tim for their answers. this is the version I could come up with which I believe is modular.
def getUserProfileFromBody(json:JsValue): Option[UserProfile] ={
val userProfile = json.asOpt[UserProfile] //check if json conforms with UserProfile structure
userProfile
}
def getJsonBody(body:AnyContent) = {
val jsonBody: Option[JsValue] = body.asJson
jsonBody
}
def generateUserKeysFromUserProfile(profile:UserProfile):UserKeys = {
val loginInfo = LoginInfo(/*provider id eg "credentials"*/CredentialsProvider.ID, /*provider Key eg email*/profile.externalProfileDetails.email)
val userKeys = UserKeys(utilities.bucketIDFromEmail(profile.externalProfileDetails.email),profile.externalProfileDetails.email,loginInfo,profile.externalProfileDetails.firstName,profile.externalProfileDetails.lastName)
logger.trace(s"generated userkey ${userKeys}")
userKeys
}
def findIfUserIsNewOrExisting(userKey:UserKeys): Future[Boolean] ={
logger.trace(s"looking for user with keys ${userKey}")
val findUserFuture = userRepo.findOne(userKey) // userFuture will eventually contain the result of database query i.e Some(user) or None
logger.trace(s"user future is ${findUserFuture}")
for(userOption <- findUserFuture) yield {
logger.trace(s"user option ${userOption}")
userOption match {
case Some(user) => {
val userConfirmed = isUserConfirmed(user)
if(userConfirmed) {
throw new DuplicateUserException(user,"duplicateuser", new Throwable("duplicateuser"))
} else throw new UnconfirmedUserException(user,"unconfirmeduser", new Throwable("unconfirmeduser"))
}
case None => {
false
}
}
}
}
def returnError(error:String) = {
println(s"returning error ${error}")
Future {
/*
note the distintion between langs and messages. Lang means languages this application supports
eg English, French
Messages are the messages defined per language. Eg app.title is a message defined inn English. It might not be defined in French
*/
/*logger.trace("langs array"+langs.availables) //languages available
logger.trace("app.title: "+messagesApi.isDefinedAt("app.title")(langs.availables(0)))//pick the first language and see taht app.title is defined in it
logger.trace("error: "+messagesApi.isDefinedAt("error.incorrectBodyType")(langs.availables(0)))//see taht error.incorrectBodyType is defined in the 1st language
*/
Ok(Json.toJson(JsonResultError(error)))
}/*TODOM - Standardise error messages. Use as constants*/
}
def isUserConfirmed(user:User):Boolean = user.profile.internalProfileDetails.get.confirmed
def sendConfirmationTokenForUser(user:User) = {
val userKeys = generateUserKeysFromUserProfile(user.profile)
//for this user, create a token which could be sent in the email for verification
val userToken:UserToken = utilities.createUserToken(user.id,userKeys,UserTokenType.RegistrationConfirmation)
logger.trace(s"saving token ${userToken}")
val userTokenSaveFuture:Future[Option[UserToken]] = userTokenRepo.save(userToken)
logger.trace(s"user token save future ${userTokenSaveFuture}")
for(userTokenOption <- userTokenSaveFuture) yield {
logger.trace(s"user token ${userTokenOption}")
userTokenOption match {
case Some(userToken) => {
val signupEmailOption:Option[SignupEmail] = createEmailMessageForUserToken(userToken)
signupEmailOption match {
case Some(signupEmail:SignupEmail) =>{
val email = mailerService.sendEmail(signupEmail.subject, signupEmail.from,List(user.profile.externalProfileDetails.email),None,Some(signupEmail.html))
println(s"sent email message ${email}")
email
//Ok(Json.toJson(JsonResultSuccess(messagesApi("success.userSignupConfirmationEmailSent")(langs.availables(0)))))
}
case None =>{
logger.trace("unable to create html response for email confirmation")
//InternalServerError(Json.toJson(JsonResultError("Internal Server Error")))
throw EmailMessageCreationException("errorInCreatingHTML", new Throwable("errorInCreatingHTML"))
}
}
}
case None => {
logger.trace("error in adding token")
throw EmailTokenSaveException("emailTokenSaveException", new Throwable("emailTokenSaveException"))
//Ok(Json.toJson(JsonResultError("user not added"))) //Todom - this is misleading as user is added but token isn't
}
}
}
}
def addUserToDatabase(user:User) = {
println(s"saving user ${user}")
val saveUserFuture:Future[Option[User]] = userRepo.save(user)
for(userOption <- saveUserFuture) yield {
userOption match {
case Some(user) => {
logger.trace("user added successfully "+user)
user
}
case None => throw new NewUserAdditionException("newUserAdditionError", new Throwable("newUserAdditionError"))
}
}
}
def addNewUser(profile:UserProfile) = {
logger.trace(s"new user sign up request with profile ${profile}")
////NOTE - //salt is empty for BCryptSha256PasswordHasher. The 'hash' method of BCryptSha256PasswordHasher does not return the salt separately because it is embedded in the hashed password.
//should creation of passwordInfo be moved to UserRepo? Not sure.
//A profile associated with the credentials provider stores a Silhouette PasswordInfo object holding the hashed password
//val passwordInfo:PasswordInfo = userRepo.passwordHasher.hash(profile.externalProfileDetails.password.get)
val passwordInfo: PasswordInfo = userRepo.hashPassword(profile.externalProfileDetails.password.get)
//logger.trace("password info is ",passwordInfo)
val bucketId = utilities.bucketIDFromEmail(profile.externalProfileDetails.email)
val loginInfo = LoginInfo(/*provider id eg "credentials"*/ CredentialsProvider.ID, /*provider Key eg email*/ profile.externalProfileDetails.email)
val newUser: User = User(
utilities.getUniqueID(), //UUID.randomUUID(),
UserProfile(Some(InternalUserProfile(loginInfo, bucketId, false, Some(passwordInfo))),
profile.externalProfileDetails))
logger.trace("adding new user" + newUser)
addUserToDatabase(newUser)
}
def saveProfileAndPortfolio(profileAndPortfolio:ExternalUserProfile) = {
logger.trace(s"saving external profile and portfolio ${profileAndPortfolio}")
val savedProfileAndPortfolioOptionFuture = userProfileAndPortfolioRepo.save(profileAndPortfolio)
for(savedProfileAndPortfolio <- savedProfileAndPortfolioOptionFuture) yield {
savedProfileAndPortfolio match {
case Some(profileAndPortfolio) => profileAndPortfolio
case None => throw ProfileAndPortfolioAdditionException("profileAndPortfolioAdditionException",new Throwable("profileAndPortfolioAdditionException"))
}
}
}
def createUserProfileAndPortfolioInformation(user:User) = {
val profile = user.profile
val initialPortfolio = user.profile.externalProfileDetails.portfolio //Some(TagsOfInterestToAUserAPI(Set(),Set(),Set()))
val profileAndPortfolio = profile.externalProfileDetails.copy(portfolio = initialPortfolio)
saveProfileAndPortfolio(profileAndPortfolio)
}
def signupUser = silhouette.UserAwareAction.async {
implicit request => {
logger.trace(s"received request ${request}")
val jsonBody = getJsonBody(request.body)
/*
TODOM - testcase - check what happens if some other body type is sent.
*/
jsonBody match {
case Some(json) => { //got json in message body.
//TODOM - convert to pretty print only if logger level is trace
val readableString: String = Json.prettyPrint(json)
logger.trace(s"received Json ${readableString}")
val userProfile = getUserProfileFromBody(json)
userProfile match {
case Some(profile) => { //json conforms to UserProfile.
logger.trace(s"received correct profile structure ${profile}")
val userKeys = generateUserKeysFromUserProfile(profile)
val res = for{isNewUser <- findIfUserIsNewOrExisting(userKeys) //this will throw error if user is duplicate
newUserDetails <- addNewUser(profile)
profileAndPortfolioInfo <- createUserProfileAndPortfolioInformation(newUserDetails)
confirmationEmail <- sendConfirmationTokenForUser(newUserDetails)} yield {
Ok(Json.toJson(JsonResultSuccess((messagesApi("success.userSignupConfirmationEmailSent"))(langs.availables(0)))))
}
res.recover {
case exception: DuplicateUserException => Ok(Json.toJson(JsonResultError(messagesApi("error.duplicateUser")(langs.availables(0)))))
case exception: UnconfirmedUserException => {
sendConfirmationTokenForUser(exception.user)
Ok(Json.toJson(JsonResultSuccess((messagesApi("success.userSignupConfirmationEmailSent"))(langs.availables(0)))))
}
case _:EmailException =>InternalServerError(Json.toJson(JsonResultError("The server encountered internal error and couldn't sent email to the email id.")))
case exception:ProfileAndPortfolioAdditionException => Ok(Json.toJson(JsonResultError("user not added")))
case _:EmailMessageCreationException => Ok(Json.toJson(JsonResultError("user not added")))
case _: EmailTokenSaveException=> Ok(Json.toJson(JsonResultError("user not added")))
case x => {
logger.error(s"unknown exception ${x}")
InternalServerError(Json.toJson(JsonResultError("Internal Server Error")))
}
}
res
}
//Json doesn't conform to UserProfile
case None => Future {
logger.trace("invalid profile structure")
Ok(Json.toJson(JsonResultError(messagesApi("error.incorrectBodyStructure")(langs.availables(0))))) } /*TODOM - Standardise error messages. Use as constants*/
}
}
//message body is not json. Error.
//TODOM - langs contains array of lang. picking the 1st one but would need too pick based on locale.
//langs.availables(0) maps to array defined in application.conf eg. - play.i18n.langs = [ "en", "en-US", "fr" ]
case None =>{
logger.trace("incorrect body type")
returnError(messagesApi("error.incorrectBodyType")(langs.availables(0)))
}
}
}
}
The most obvious change is to put code in functions. By breaking up the logic into functions with meaningful names you make the code much clearer to read. If you define them as local functions within the method they can access context from the method without it having to be passed in as parameters.
Put all the error returns in static vals outside the method rather than creating them each time.
Consider using Option.fold rather than match:
option.fold(error){ result =>
// Further code
}
Remove the {} after case x =>, they are not required.
The first case in case x => { x match { is redundant.
And don't worry about performance until you can prove that the performance of this section of code has a material effect on the behaviour of the application.
One problem is that you are not using the Failure case of Future to indicate failure so you can't use map/flatMap to chain operations that might fail.
Create your own subclass of Exception and have the methods return Future.failed(MyException(error)) on failure rather than Future(error)
Then do this
val res =
for {
res1 <- futureAction1
res2 <- futureAction2(res1)
res3 <- futureAction3(res2)
} yield {
res3
}
and finally
res.recover{ case MyException(err) => err }
The for will stop at the first Failure return value, and the recover will turn this into the appropriate Success value.

Unable to use for comprehension to resolve Future

I have this Action which should return a Future[Result] but I am unable to code it using for comprehension. This is the first time I am using for comprehension so I am also not sure if this is how I should use for.
Also, would someone comment on whether the usage of for is correct?
def verifyUser(token:String) = Action.async{
implicit request => { //the function takes a token
val tokenFutureOption:Future[Option[UserToken]] = userTokenRepo.find(UserTokenKey(UUID.fromString(token))) //checkc if the token exists in the db (db returns a Future)
for(tokenOption<- tokenFutureOption) yield { //resolve the future
tokenOption match {
case Some(userToken) =>{//token exists
val userOptionFuture = userRepo.findUser(userToken.loginInfo)//find user to which the token belongs. Another db request which returns a Future
for(userOption <- userOptionFuture) yield {//resolve future
userOption match {
case Some(user) =>{//user exists
val newInternalProfile = user.profile.internalProfileDetails.get.copy(confirmed=true) //modify user's profile
val newProfile = UserProfile(Some(newInternalProfile),user.profile.externalProfileDetails)
val confirmedUser = user.copy(profile=newProfile)
val userOptionFuture :Future[Option[User]] = userRepo.updateUser(confirmedUser) //update profile with new value. Another db operation with returns a Future
for(userOption <- userOptionFuture) yield {//resolve future
userTokenRepo.remove(UserTokenKey(UUID.fromString(token)))//remove the token
// Ok("user verified") //I WANT TO RETURN SUCCESS RESPONSE HERE BUT CODE DOESN'T COMPILE IF I UNCOMMENT THIS
}
}
case None =>{ //user doesn't exist
// Ok("user verified") //I WANT TO RETURN FAILURE RESPONSE HERE BUT CODE DOESN'T COMPILE IF I UNCOMMENT THIS
}
}
}
}
case None =>{//INVALID TOKEN RECEIVED
Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config //I CAN RETURN Redirect (WHICH IS OF SAME TYPE AS OK I.E. RESULT) BUT WHY AM I NOT ABLE TO USE OK ABOVE
}
}
}//THIS IS THE END OF FIRST FOR LOOP. HOW DO I HANDLE FAILURES IN THE FUTURE?
}
}
You are using Future, Option which are Monads and the idea behind them being helpful to sequence the computations just like Functors but also with capability to specify what happens next. .flatMap is what Monads have allow that which can be used as for yield for readability.
So in your example you can compose the api using sequence of operations.
object Api {
final case class UserTokenKey(uuid: UUID)
final case class UserToken(loginInfo: String)
object userTokenRepo {
def find(u: UserTokenKey) = {
Future.successful(Some(UserToken(loginInfo = "foundLoginInfo")))
}
def remove(u: UserTokenKey) = {
Future.successful(Some(UserToken(loginInfo = "")))
}
}
final case class User(profile: String)
object userRepo {
def findUser(u: String) = {
Future.successful(Some(User("profile")))
}
def updateUser(u: User) = {
Future.successful(Some(User("updated profile")))
}
}
def verifyUser(token: String)(implicit executionContext: ExecutionContext) = {
val user: Future[Option[UserToken]] = for {
tokenMaybe: Option[UserToken] <- userTokenRepo.find(UserTokenKey(UUID.fromString(token)))
userMaybe: Option[User] <-
tokenMaybe match {
case Some(t) => userRepo.findUser(t.loginInfo)
case _ => Future.successful(Option.empty[User])
}
updatedUserMaybe: Option[User] <-
userMaybe match {
case Some(u) => userRepo.updateUser(u)
case _ => Future.successful(Option.empty[User])
}
removedUserMaybe: Option[UserToken] <- userTokenRepo.remove(UserTokenKey(UUID.fromString(token)))
} yield removedUserMaybe
user
}
def httpLayer(request: String) = {
import scala.concurrent.ExecutionContext.Implicits.global
import play.api.mvc.Results
verifyUser(request).onComplete {
case Success(Some(user)) =>
println("user verified")
Results.Ok("user verified")
case Success(None) =>
println("user does not exist")
Results.Ok("user does not exist")
case Failure(f) =>
println("unknown error")
Results.Ok("unknown error")
}
}
}
Now you can test your api as below
def main(args: Array[String]): Unit = {
import Api._
httpLayer(UUID.randomUUID().toString) // user verified
}
Note1: you might want to use api to respond Future[Either[Error, User]] to better handle errors and use Error to determine what to respond back to the http consumers.
Note2: Since you are working with two monads Future[Option[a]], you can combine them using Monad Transformation OptionT[OuterMonad, Value Type] in scalaz or cats mtl library. Which gives a single monad OptionT.
def verifyUserV2(tokenString: String)(implicit executionContext: ExecutionContext) = {
import scalaz._
import Scalaz._
// import cats.implicits._
// import cats.data.OptionT
val userStack = for {
token <- OptionT(userTokenRepo.find(UserTokenKey(UUID.fromString(tokenString))))
user <- OptionT(userRepo.findUser(token.loginInfo))
updatedUser <- OptionT(userRepo.updateUser(user))
removedUser <- OptionT(userTokenRepo.remove(UserTokenKey(UUID.fromString(tokenString))))
} yield removedUser
userStack.run
//cats unpack stack
// userStack.value
}
Useful reads:
Futures - map vs flatmap
Using Either to process failures in Scala code

How to refactor this scala code?

I want to refactor this code into something more readable and better in general. I know that in Scala there are normally neat ways of doing things but for me it's getting a bit messy (BTW I'm using the Play library in the code). this is a snippet of my code:
class HomeController #Inject()
(cc: ControllerComponents)
(implicit val config: Configuration)
extends AbstractController(cc) {
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {
implicit request: Request[String] =>
val promise = Promise[Result]()
Future {
val removeId = request.body.toLong
println(s"remove id $removeId")
promise completeWith {
idElementsDAO.remove(removeId, uid.toLong) map {
_ => Ok("")
} recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
BadRequest(errorMessage)
}
}
} recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
promise.success(BadRequest(errorMessage))
}
promise.future
}
}
Assuming that idElementsDAO.remove return a Future, this is probably more idiomatic:
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {implicit request =>
val removeId = request.body.toLong
println(s"remove id $removeId")
idElementsDAO.remove(removeId, uid.toLong)
.map(_ => NoContent) // probably more correct than `Ok("")`
.recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
BadRequest(errorMessage)
}
}
No need for the Promise or the call to Future {...} (Future.apply).
Keep in mind, it's probably not the best idea to directly pass the underlying error of any Throwable directly to the http client (browser?).
If you add generic error handling code to the global error handler (for unexpected errors) that logs the error and sends a generic message to the front-end, you can then write it even cleaner like this:
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {implicit request =>
val removeId = request.body.toLong
println(s"remove id $removeId")
for {
_ <- idElementsDAO.remove(removeId, uid.toLong)
} yield NoContent
}
https://www.playframework.com/documentation/2.6.x/ScalaErrorHandling
Here is a simpler version of your code:
class HomeController #Inject()(cc: ControllerComponents)(implicit val config: Configuration)
extends AbstractController(cc) {
def removeIdElement(uid: String) = HAction(uid, "AuthEvent", 1, "login", parse.text).async {
implicit request: Request[String] =>
Future {
val removeId = request.body.toLong
println(s"Removing id $removeId")
removeId
}.flatMap(id => idElementsDAO.remove(id, uid.toLong))
.map(_ => Ok(""))
.recover {
case t: Throwable =>
val errorMessage = getMessageFromThrowable(t)
println(s"Removing id element failed: ${errorMessage}")
BadRequest(errorMessage)
}
}
}
In the above code, a Promise is not needed, and the recover combinator is not repeated.

scala - convert byte[] to case class instance

I stored one of my case class instance 'Alert' as binary in mongoDB.
I have to read and type cash to 'Alert'.
I tried this
object MongoMain extends App {
val uri = new MongoURI("url")
val mongoColl = MongoConnection(uri)("testdb")("alert")
val q = MongoDBObject("_id" -> ObjectId.massageToObjectId("5269c718ebb2e54b950a1cc1"))
// println(mongoColl.findOne(q))
mongoColl.find(q).foreach { z ⇒
z.get("message").getClass match {
case data: Class[Binary] ⇒ println(data.getSimpleName)
case _ ⇒
}
}
}
This printing byte[], ie, it retriving message field as byte[] after I have to convert it to Alert. how can I do that, need help:
Try to use the follow function,hope it helpful.
def deserializeAlert(data: Array[Byte]): Alert ={
try {
val in = new ObjectInputStream(new ByteArrayInputStream(data))
val alert = in.readObject.asInstanceOf[Alert]
in.close()
alert
}
catch {
case cnfe: ClassNotFoundException => {
cnfe.printStackTrace()
null
}
case ioe: IOException => {
ioe.printStackTrace()
null
}
}
}