return type for findAndModify with futures - scala

Say I'm using an async library to read and write data. If a failure, I get an error message as a string, otherwise the data. Is there a better way to represent the return type than Future[Either[String, Future[Data]]]?
Possible implementation:
def createData(name: String): Future[Either[String, Future[Data]]] = {
dataDAO.findOneByName(name).flatMap {
case Some(data) =>
Future.successful(Left(s"Data with name already exists. $name"))
case None =>
val data = Data.createFromName(name)
dataDAO.save(data).map {
lastError =>
data
}.right.futureSuccessful
}
}

Your return type should be Future[Either[String, Data]]
To archive this you need to change your second case to:
case None =>
val data = Data.createFromName(name)
dataDAO.save(data).map {
_ => Right(data)
}
I even would improve your error type from String to e.g. CreateDataError, so your return type would be Future[Either[CreateDataError, Data]] and your first case would be
// Having somewhere decalred
trait CreateDataError
object DataAlreadyExistsError
//...
//...
case Some(data) =>
Future.successful(Left(DataAlreadyExistsError))

Related

Scala - Return value from an anonymous function

I've a named function that as an Either[A, List[B]] as return type. Inside that named function, I have a anonymous function that I'd like to make it return a value to the named function. Instead I keep getting the error "Non local returns are no longer supported; use scala.util.control.NonLocalReturns instead".
I did some research but wasn't able to find a solution.
Am I missing something? Is there any to workaround this problem?
Here's my code
def readEvents(SchedulingNode: Node): Either[Error, List[Event]] =
val events: List[Either[Error, Event]] = SchedulingNode.child
.filter(n => n.label == EVENT_NODE_NAME)
.map(n => {
val head: Head = n.attribute(HEAD_ATTRIBUTE) match {
case None => return Left(EmptyProperty("head")) //Error occurs here
case Some(s) => Head.from(s.toString()) match
case Left(e: Error) => return Left(e); //Here also
case Right(h: Head) => h
}
Event.from(head, 1) match {
case Left(e: Error) => Left(e);
case Right(ev: Event) => Right(ev)
}
}).toList
//Rest of the code...

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.

playframework scala returning post request

in my scala playframework application I want to return the via Post submitted and then with slick stored object back to frontend as json
I tried this:
def createClient = Action.async { implicit request =>
request.body.asJson.map(_.validate[ClientModel] match {
case JsSuccess(client) =>
clientDTO.createClient(client).map { clients =>
Ok(Json.toJson(clients))
}
})
}
but I get this error:
what could be my problem?
NEW ERROR
Try with something along these lines:
def createClient = Action.async { implicit request =>
request.body.asJson match {
case None => // do something that returns a Future[Result] ~ such as NotFound or
case Some(js) =>
js.validate[ClientModel] match {
case client: JsSuccess[ClientModel] =>
clientDTO.createClient(client).map { clients =>
Ok(Json.toJson(clients))
}
case e: JsError => // do something that returns a Future[Result] ~ such as InternalServerError
}
}
}
the .async, as the name suggests require a Future type.
You have 2 options:
Remove the .async, this will make your def synchronous (deprecated)
Leave the .async but return a Future result
def createClient = Action.async { implicit request =>
request.body.asJson.map(_.validate[ClientModel] match {
case JsSuccess(client) =>
clientDTO.createClient(client).map { clients =>
Future(Ok(Json.toJson(clients)))
}
})
}
But you still need to add the case of validation error:
case JsSuccess(client) =>
{
clientDTO.createClient(client).map
{
clients => Future(Ok(Json.toJson(clients)))
}
}
case _ => Future(BadRequest(""))
This should work and, in all the cases apart JsSuccess, the function will return a future BadRequest response.
A better solution is to change the _ with JsError:
case e: JsError =>
{
Println(e)
Future(BadRequest(.....))
}
This will also print the error.
You can read more here: https://www.playframework.com/documentation/2.6.x/ScalaJson
(Using validation chapter)
More about future in scala: https://docs.scala-lang.org/overviews/core/futures.html

ReactiveMongo query returning None

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.

How to properly use future/actorSelection/resolveOne in Play app?

Below is my code:
def runAsync(crewType: String) = Action.async {
val temp: Future[Result] = Future(Crew.findCaptainByCrewType(crewType) match {
case None =>
BadRequest(s"Invalid crew name provided: $crewType")
case Some(crew) =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map { actorRef =>
Ok(println("hi hi"))
}
})
temp
}
I don't understand why this doesn't work?
My objective is to have user pass in a name, which then I try to find an actor with that name using the actorSelection and resolveOne. I assume I am using it incorrectly?!
ActorSelection.resolveOne() returns a Future[ActorRef], and because you are inside a Future(...) you get a Future[Future[Result]] in case of a valid crewname.
You can solve this by using flatMap in which case you should also return a Future in case of an invalid crewname (None).
Unrelated: You can also leave out the temp value and can replace the pattern matching on Option by Option.fold.
def runAsync(crewType: String) = Action.async {
Future(Crew.findCaptainByCrewType(crewType)).flatMap( _.fold(
Future.successful(BadRequest(s"Invalid crew name provided: $crewType"))
)( crew =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map {
actorRef => Ok(println("hi hi")) // not sure you want println here
}
))
}