ReactiveMongo Find One Document Error - mongodb

With the following code bit, I'm trying to fetch a single document based on a user's email where email is part of the stored document:
def userByEmail(encryptedEmail: String): Future[Either[ServiceError, User]] = async {
println(s"finding a user for email $encryptedEmail")
val inlandDb = dbConn.db(dbName)
val userColl = inlandDb[BSONCollection](userCollection)
val found = await(
userColl.find(BSONDocument(emailKey -> BSONString(encryptedEmail))).one[User]
)
println(s"found a user $found")
found match {
case Some(user) => Right(user)
case None => Left(ServiceError("user not found"))
}
}
The user for the given email exists as I verified it in the mongo console. Is there anything wrong? Why is that I'm not able to get the user back for my search query.
Should I have any index defined on email in my user document so that it is searchable?
I get the following error:
finding a user for email Ctkiaw/cbW8DxtRIxbtUYADq5bp6uW7tVryhpT57lKU=
failed java.lang.RuntimeException: None.get
None.get
java.lang.RuntimeException: None.get
at scala.sys.package$.error(package.scala:27)
at play.api.libs.iteratee.Iteratee$$anonfun$run$1.apply(Iteratee.scala:396)
at play.api.libs.iteratee.Iteratee$$anonfun$run$1.apply(Iteratee.scala:389)
at play.api.libs.iteratee.StepIteratee$$anonfun$fold$2.apply(Iteratee.scala:706)
at play.api.libs.iteratee.StepIteratee$$anonfun$fold$2.apply(Iteratee.scala:706)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at play.api.libs.iteratee.Execution$trampoline$.executeScheduled(Execution.scala:109)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:71)
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:248)
at scala.concurrent.Promise$class.complete(Promise.scala:55)
at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:153)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:23)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.pollAndExecAll(ForkJoinPool.java:1253)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1346)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Here is my User model:
case class User(
_id: Option[String],
firstName: String,
lastName: String,
email: String,
pass: String,
address: Address,
createDate: DateTime,
activateDate: Option[DateTime],
isUserActivated: Boolean,
verificationDate: Option[DateTime]
)
Here is how I do the transformation:
implicit object UserBSONHandler
extends BSONDocumentReader[User] with BSONDocumentWriter[User] {
def read(doc: BSONDocument): User = {
User(
_id = doc.getAs[String]("_id"),
createDate = doc.getAs[BSONDateTime](createDateKey).map(dt => new DateTime(dt.value, DateTimeZone.UTC)).get,
activateDate = doc.getAs[BSONDateTime](activateDateKey).map(dt => new DateTime(dt.value, DateTimeZone.UTC)),
verificationDate = doc.getAs[BSONDateTime](verificationDateKey).map(dt => new DateTime(dt.value, DateTimeZone.UTC)),
firstName = doc.getAs[String](firstNameKey).get,
lastName = doc.getAs[String](lastNameKey).get,
email = doc.getAs[String](emailKey).get,
pass = doc.getAs[String](passKey).get,
address = doc.getAs[Address](addressKey).get,
isUserActivated = doc.getAs[Boolean](isUserActivatedKey).get
)
}
def write(user: User): BSONDocument = {
BSONDocument(
firstNameKey -> user.firstName,
lastNameKey -> user.lastName,
emailKey -> user.email,
passKey -> user.pass,
addressKey -> user.address,
createDateKey -> user.createDate.toString(Iso8601DateFormatter),
activateDateKey -> user.activateDate.map(dt => dt.toString(Iso8601DateFormatter)),
verificationDateKey -> user.verificationDate.map(dt => dt.toString(Iso8601DateFormatter)),
isUserActivatedKey -> user.isUserActivated
)
}
}

From the logs it looks like something is off in your Reads the original code posted above is syntactically OK but the error message along with the logging code helped me zero in on further checking out you BSONDocumentReader

With help from "Barry", I was able to nail down where the problem was. I had to change the read method as below:
def read(doc: BSONDocument): User = {
User(
_id = doc.getAs[String]("_id"),
createDate = doc.getAs[String](createDateKey).map(dt => new DateTime(dt, DateTimeZone.UTC)).get,
activateDate = doc.getAs[String](activateDateKey).map(dt => new DateTime(dt, DateTimeZone.UTC)),
verificationDate = doc.getAs[String](verificationDateKey).map(dt => new DateTime(dt, DateTimeZone.UTC)),
firstName = doc.getAs[String](firstNameKey).get,
lastName = doc.getAs[String](lastNameKey).get,
email = doc.getAs[String](emailKey).get,
pass = doc.getAs[String](passKey).get,
address = doc.getAs[Address](addressKey).get,
isUserActivated = doc.getAs[Boolean](isUserActivatedKey).get
)
}

Related

Scala Test: File upload with additional attributes - MultipartFormData

I am actually trying to test the creation of a new product.
One attribute of a product is a picture. This picture should be stored into a directory called "images". In the database only the file name should be stored as a string in the picture column.
So I tried to create a MultiPartFormData Fake Request and add the attributes into the dataParts attribute of the MultiPartFormData.
But when executing the test i get following error:
\test\InventoryControllerSpec.scala:50: Cannot write an instance of play.api.mvc.MultipartFormData[play.api.
libs.Files.TemporaryFile] to HTTP response. Try to define a Writeable[play.api.mvc.MultipartFormData[play.api.libs.Files.TemporaryFile]]
The product model looks like following:
case class Product(id: Option[Int],
name: String,
category: String,
picture: Option[String],
amount: Int,
criticalAmount: Int
) {
}
object Product {
implicit val productFormat = Json.format[Product]
def tupled(t: (Option[Int], String, String, Option[String], Int, Int)) =
Product(t._1, t._2, t._3, t._4, t._5, t._6)
def toTuple(p: Product) = Some((p.id, p.name, p.category, p.picture, p.amount, p.criticalAmount))
}
The database model looks like this:
class Products(tag: Tag) extends Table[Product](tag, "PRODUCTS"){
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def name = column[String]("NAME")
def category = column[String]("CATEGORY")
def picture = column[String]("PICTURE")
def amount = column[Int]("AMOUNT")
def criticalAmount = column[Int]("CRITICALAMOUNT")
def * = (id.?, name, category, picture.?, amount, criticalAmount) <>(Product.tupled, Product.toTuple)
}
I think also the create function in the controller should work:
val productForm = Form(
tuple(
"name" -> nonEmptyText,
"category" -> nonEmptyText,
"amount" -> number,
"criticalAmount" -> number
)
)
def create = SecuredAction(IsInventoryAdmin()
).async(parse.multipartFormData) {
implicit request => {
val pr : Option[Product] = productForm.bindFromRequest().fold (
errFrm => None,
product => Some(Product(None, product._1, product._2, None, product._3,product._4))
)
request.body.file("picture").map { picture =>
pr.map { product =>
val filename = picture.filename
val contentType = picture.contentType
val filePath = s"/images/$filename"
picture.ref.moveTo(new File(filePath), replace=true)
val fullProduct = product.copy(picture = Some(filePath))
inventoryRepo.createProduct(fullProduct).map(p => Ok(Json.toJson(p)))
}.getOrElse{
Future.successful(
BadRequest(Json.obj("message" -> "Form binding error.")))
}
}.getOrElse {
Future.successful(
BadRequest(Json.obj("message" -> "File not attached.")))
}
}
}
Now my problem is the creation of a Scala Test which checks if the functionality is working. At the moment my code looks like this:
"allow inventory admins to create new products" in new RepositoryAwareContext {
new WithApplication(application) {
val token = CSRF.SignedTokenProvider.generateToken
val tempFile = TemporaryFile(new java.io.File("/images/the.file"))
val part = FilePart[TemporaryFile](key = "the.file", filename = "the.file", contentType = Some("image/jpeg"), ref = tempFile)
val formData = MultipartFormData(dataParts = Map(("name", Seq("Test Product")),("category", Seq("Test Category")),("amount", Seq("50")), ("criticalAmount", Seq("5"))), files = Seq(part), badParts = Seq(), missingFileParts = Seq())
val result = route(FakeRequest(POST, "/inventory", FakeHeaders(), formData)
.withAuthenticator[JWTAuthenticator](inventoryAdmin.loginInfo)
.withHeaders("Csrf-Token" -> token)
.withSession("csrfToken" -> token)
).get
val newInventoryResponse = result
status(newInventoryResponse) must be(OK)
//contentType(newInventoryResponse) must be(Some("application/json"))
val product = contentAsJson(newInventoryResponse).as[Product]
product.id mustNot be(None)
product.name mustBe "Test Product"
product.category mustBe "Test Category"
}
}
It would be great if anybody can help me because i can not find a solution on my own...
Kind regards!

What is this scala syntax: "users returning users.map(_._id)"

Can anyone explain the 2nd to last line? What is users returning...? Thanks.
val users: TableQuery[Users] = TableQuery[Users]
def create(data: Map[String, Any]): Int = {
userId.withSession {
implicit session =>
val userObj = new User(
fullname = data("fullname").toString,
username = data("username").toString,
password = data("password").toString)
(users returning users.map(_._id)) += userObj // What is this doing?
0
}
}

Why does compilation fail with "not found: value Users"?

I want to retrieve a row from my default database postgres. I have table "Users" defined already.
conf/application.conf
db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://localhost:5234/postgres"
db.default.user="postgres"
db.default.password=""
controllers/Application.scala
package controllers
import models.{UsersDatabase, Users}
import play.api.mvc._
object Application extends Controller {
def index = Action {
Ok(views.html.index(UsersDatabase.getAll))
}
}
models/Users.scala
package models
import java.sql.Date
import play.api.Play.current
import play.api.db.DB
import slick.driver.PostgresDriver.simple._
case class User(
id: Int,
username: String,
password: String,
full_name: String,
email: String,
gender: String,
dob: Date,
joined_date: Date
)
class Users(tag: Tag) extends Table[User](tag, "Users") {
def id = column[Int]("id")
def username = column[String]("username", O.PrimaryKey)
def password = column[String]("password")
def full_name = column[String]("full_name")
def email = column[String]("email")
def gender = column[String]("gender")
def dob = column[Date]("dob")
def joined_date = column[Date]("joined_date")
def * = (id, username, password, full_name, email, gender, dob, joined_date) <> (User.tupled, User.unapply)
}
object UsersDatabase {
def getAll: List[User] = {
Database.forDataSource(DB.getDataSource()) withSession {
Query(Users).list
}
}
}
While accessing http://localhost:9000/ it gives compilation error:
[error] .../app/models/Users.scala:36: not found: value Users
[error] Query(Users).list
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
How to resolve this error and access data properly?
The compilation error message says it all - there's no value Users to use in the scope.
Change the object UsersDatabase to look as follows:
object UsersDatabase {
val users = TableQuery[Users]
def getAll: List[User] = {
Database.forDataSource(DB.getDataSource()) withSession { implicit session =>
users.list
}
}
}
And the error goes away since you're using the local val users to list users in the database.
As described in Querying in the official documentation of Slick session val is an implicit value of list (as final def list(implicit session: SessionDef): List[R]), and hence implicit session in the block:
All methods that execute a query take an implicit Session value. Of
course, you can also pass a session explicitly if you prefer:
val l = q.list(session)

Eager fetch using Anorm

I'm trying to do eager fetch with Anorm, this is lazy fetch like here https://gist.github.com/guillaumebort/2788715
package models
import java.util.Date
import anorm._
import anorm.SqlParser._
import play.api.db.DB
import play.api.Play.current
// table users
case class User(id: Option[Long] = None, firstName: Option[String], lastName: Option[String]) {
// mismatch types Set[Nicknames] and List[Nicknames] because * returns List
lazy val nickNames: Set[Nicknames] = DB.withConnection { implicit connection =>
SQL"""
SELECT * FROM nicknames
JOIN types t ON nicknames.type_id = t.id
JOIN events e ON nicknames.events_id = e.id
WHERE user.id = $id
""".as(Nickname.withTypeAndEvents.*)
}
}
object User {
val simple = {
get[Option[Long]]("users.id") ~
get[Option[String]]("users.first_name") ~
get[Option[String]]("users.last_name") map {
case id ~ firstName ~ lastName => User(id, firstName, lastName)
}
}
def findById(id: Long): Option[User] = DB.withConnection { implicit connection =>
SQL"SELECT * FROM users WHERE id = $id".as(simple.singleOpt)
}
}
// table nicknames
case class Nickname(id: Option[Long] = None, name: Option[String], startDate: Option[Date],
nType: Option[NickType] = None,
user: Option[User] = None,
events: Option[Set[Event]] = None) // I use Set here to remove duplicates
object Nickname {
val simple = {
get[Option[Long]]("nicknames.id") ~
get[Option[String]]("nicknames.name") ~
get[Option[Date]]("nicknames.start_date") map {
case id ~ name ~ startDate => Nickname(id, name, startDate)
}
}
val withTypeAndUser = simple ~ NickType.simple ~ User.simple map {
case nick ~ nType ~ user => nick.copy(nType = Some(nType), user = Some(user))
}
val withTypeAndEvents = withTypeAndUser ~ Event.simple map {
// it's wrong I think
case nick ~ event => nick.copy(events = Some(Set(event)))
}
}
// table types
case class NickType(id: Option[Long] = None, name: Option[String])
object NickType {
val simple = get[Option[Long]]("types.id") ~ get[Option[String]]("types.name") map {
case id ~ name => NickType(id, name)
}
}
// table events
case class Event(id: Option[Long] = None, regDate: Option[Date], description: Option[String],
user: Option[User] = None)
object Event {
val simple = {
get[Option[Long]]("events.id") ~
get[Option[Date]]("events.reg_date") ~
get[Option[String]]("events.description") map {
case id ~ regDate ~ description => Event(id, regDate, description)
}
}
}
I need that findById return my User with nicknames using eager fetch not lazy.
User -> Nicknames use One To Many relation
Nicknames -> User use Many To One relation join column user_id
Nicknames -> Events use One To Many relation
Events -> Nicknames use Many To One relation join column event_id

Strange result when using squeryl and scala

I'm trying to select the coupled user by getting the correct linkedAccount.
The query that is created is correct but when trying to use a property
on dbuser e.g dbuser.lastName I get a compile error since dbuser is not
of type User but Query1 size=?
It's probably something really simple but I can't figure it out since I'm
a scala and squeryl noob!
Why doesn't it return the correct value and what have I done wrong in my query?
Also, saving works without any issues.
User:
class User(
#Column("id") val id: Long,
#Column("first_name") val firstName : String,
#Column("last_name") val lastName : String,
#Column("email") val email : String,
#Column("email_validated") val emailValidated: Boolean = false,
#Column("last_login") val lastLogin: Timestamp = null,
val created: Timestamp,
val modified: Timestamp,
val active: Boolean = false
) extends KeyedEntity[Long] {
lazy val linkedAccounts: OneToMany[LinkedAccount] = AppDB.usersToLinkedAccounts.left(this)
}
LinkedAccount:
class LinkedAccount(
#Column("id") val id: Long,
#Column("user_id") val userId: Long,
#Column("provider_user_id") val providerUserId: String,
#Column("salt") val salt: String,
#Column("provider_key") val providerKey: String) extends KeyedEntity[Long] {
lazy val user: ManyToOne[User] = AppDB.usersToLinkedAccounts.right(this)
}
AppDB:
object AppDB extends Schema {
val users = table[User]("users")
val linkedAccounts = table[LinkedAccount]("linked_account")
val usersToLinkedAccounts = oneToManyRelation(users, linkedAccounts).via((u, l) => u.id === l.userId)
def userByLinkedAccount(prodivderKey: String, providerUserId: String) = {
from(AppDB.users)(u =>
where(u.id in
from(AppDB.linkedAccounts)(la =>
where(la.userId === u.id and la.providerKey === prodivderKey and la.providerUserId === providerUserId)
select (la.userId)
)
)
select (u)
)
}
The call:
val dbuser = inTransaction {
val u2 = AppDB.userByLinkedAccount(id.providerId, id.id)
println(u2.statement)
}
println(dbuser.lastName)
The sql generated
Select
users10.last_login as users10_last_login,
users10.email as users10_email,
users10.modified as users10_modified,
users10.last_name as users10_last_name,
users10.first_name as users10_first_name,
users10.id as users10_id,
users10.created as users10_created,
users10.email_validated as users10_email_validated,
users10.active as users10_active
From
users users10
Where
(users10.id in ((Select
linked_account13.user_id as linked_account13_user_id
From
linked_account linked_account13
Where
(((linked_account13.user_id = users10.id) and (linked_account13.provider_key = 'facebook')) and (linked_account13.provider_user_id = 'XXXXXXXXXX'))
) ))
BTW, in the documentation to #Column and #ColumnBase it is said:
The preferred way to define column metadata is not not define them (!)
So, you can define columns just as
val id: Long,
instead of
#Column("id") val id: Long
Ok figured it out. I need to make the call, in this case:
.headOption
Also fixed the query after some tips from Per
def userByLinkedAccount(providerKey : String, providerUserId : String) = {
inTransaction {
from(AppDB.users, AppDB.linkedAccounts)((u,la) =>
where (u.id === la.userId and la.providerKey === providerKey and la.providerUserId === providerUserId)
select(u)
).headOption
}
}