Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error on dummy user creation #359

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ docker rm drops

>
*Notice:*
All server generated output will be written to the `server-output` file.
All server generated output will be written to the command line (logs in the case of a docker image).
This is also needed to confirm users since the production server also uses a mock
mail server.
>
Expand Down Expand Up @@ -365,6 +365,9 @@ and your service.
ChangeLog
=========

## Version 0.36.66 (2021-5-6)
* [[B] #4 - UserDAO does not execute inserts as a transaction](https://github.com/SOTETO/drops/issues/4)
* [[B] #6 - DummyUser creation throws an error on Insert](https://github.com/SOTETO/drops/issues/6)

## Version 0.36.61 (2019-4-16)

Expand Down
1 change: 1 addition & 0 deletions app/controllers/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class Application @Inject() (
// save user in database and return a future of the saved user
userService.save(crewSupporter)
})
//Future.sequence(users).map((list) => Ok(Json.toJson(list)))
Future.sequence(users).map((list) => Ok(Json.toJson(list)))
})
)
Expand Down
23 changes: 18 additions & 5 deletions app/controllers/webapp/Auth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,34 @@ class Auth @Inject() (
case Some(_) =>
Future.successful(WebAppResult.Bogus(request, "error.userExists", List(signUpData.email), "AuthProvider.SignUp.UserExists", Json.toJson(Map[String, String]())).getResult)
case None =>
val profile = Profile(loginInfo, signUpData.email, signUpData.firstName, signUpData.lastName, signUpData.mobilePhone, signUpData.placeOfResidence, signUpData.birthday, signUpData.gender, signUpData.address)
val profile = Profile(
loginInfo,
signUpData.email,
signUpData.firstName,
signUpData.lastName,
signUpData.mobilePhone,
signUpData.placeOfResidence,
signUpData.birthday,
signUpData.gender,
signUpData.address.flatMap(a => a.isEmpty match {
case true => None
case _ => Some(a)
})
)
for {
avatarUrl <- avatarService.retrieveURL(signUpData.email)
user <- userService.save(User(id = UUID.randomUUID(), List(profile), updated = System.currentTimeMillis(), created = System.currentTimeMillis()))
pw <- authInfoRepository.add(loginInfo, passwordHasher.hash(signUpData.password))
token <- userTokenService.save(UserToken.create(user.id, signUpData.email, true))
token <- userTokenService.save(UserToken.create(user.get.id, signUpData.email, true))
} yield {
getWebApp match {
case Left(message) => WebAppResult.NotFound(request, message._1, Nil, "AuthProvider.SignUp.MissingConfig", Map[String, String]()).getResult
case Right(webapp) => {
mailer.welcome(profile, link = webapp.getAbsoluteSignUpTokenEndpoint(token.id.toString))
WebAppResult.Ok(request, "signup.created", Nil, "AuthProvider.SignUp.Success", Json.toJson(profile)).getResult
}
mailer.welcome(profile, link = webapp.getAbsoluteSignUpTokenEndpoint(token.id.toString))
WebAppResult.Ok(request, "signup.created", Nil, "AuthProvider.SignUp.Success", Json.toJson(profile)).getResult
}
}
}
}
}
)
Expand Down
167 changes: 147 additions & 20 deletions app/daos/UserDao.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ trait UserDao extends ObjectIdResolver with CountResolver{
// def getObjectId(userId: UUID):Future[Option[ObjectIdWrapper]]
def find(loginInfo:LoginInfo):Future[Option[User]]
def find(userId:UUID):Future[Option[User]]
def save(user:User):Future[User]
def replace(user:User):Future[User]
def save(user:User):Future[Option[User]]
def replace(user:User):Future[Option[User]]
def confirm(loginInfo:LoginInfo):Future[User]
def link(user:User, profile:Profile):Future[User]
def update(profile:Profile):Future[User]
def link(user:User, profile:Profile):Future[Option[User]]
def update(profile:Profile):Future[Option[User]]
def list : Future[List[User]]
def listOfStubs : Future[List[UserStub]]
def delete (userId:UUID):Future[Boolean]
Expand Down Expand Up @@ -110,17 +110,26 @@ class MariadbUserDao @Inject()(val crewDao: MariadbCrewDao) extends UserDao {
* @param user
* @return
*/
override def save(user: User): Future[User]={
override def save(user: User): Future[Option[User]]={
val userDBObj = UserDB(user)

dbConfig.db.run((users returning users.map(_.id)) += userDBObj).flatMap(id => {
findDBUserModel(id)
}).flatMap(userObj => {
addProfiles(userObj, user.profiles)
})
val crewIds : Future[Map[String, Option[Long]]] = Future.sequence(user.profiles.map(profile =>
profile.supporter.crew match {
// if supporter has a crew, find the CrewDB.id by UUID crew.id
case Some(crew) => crewDao.findDBCrewModel(crew.id).map(_ match {
case Some(c) => (profile.loginInfo.providerKey, Some(c.id))
case None => (profile.loginInfo.providerKey, None)
})
//else None
case _ => Future.successful((profile.loginInfo.providerKey, None))
}
)).map(_.toMap)
crewIds.flatMap(c_ids =>
dbConfig.db.run(insertUser(userDBObj, user.profiles, c_ids))
).flatMap(id => find(id))
}

override def replace(updatedUser: User): Future[User] = {
override def replace(updatedUser: User): Future[Option[User]] = {
findDBUserModel(updatedUser.id).flatMap(user => {
//Delete Profiles
val deleteProfile = profiles.filter(_.userId === user.id)
Expand All @@ -132,9 +141,25 @@ class MariadbUserDao @Inject()(val crewDao: MariadbCrewDao) extends UserDao {
dbConfig.db.run(
(deletePasswordInfo.delete andThen deleteLoginInfo.delete andThen deleteAddress.delete andThen deleteSupporterCrew.delete andThen
deleteSupporter.delete andThen deleteProfile.delete).transactionally
).flatMap(_ =>
addProfiles(user, updatedUser.profiles)
)
).flatMap(_ => {
// now insert the new profiles
// start with retrieving the crew IDs of the assigned crews
val crewIds: Future[Map[String, Option[Long]]] = Future.sequence(updatedUser.profiles.map(profile =>
profile.supporter.crew match {
// if supporter has a crew, find the CrewDB.id by UUID crew.id
case Some(crew) => crewDao.findDBCrewModel(crew.id).map(_ match {
case Some(c) => (profile.loginInfo.providerKey, Some(c.id))
case None => (profile.loginInfo.providerKey, None)
})
//else None
case _ => Future.successful((profile.loginInfo.providerKey, None))
}
)).map(_.toMap)
crewIds.flatMap(c_ids =>
dbConfig.db.run(insertProfiles(user.id, updatedUser.profiles, c_ids))
).flatMap(_ => find(user.id))
//addProfiles(user, updatedUser.profiles)
})
})
}

Expand All @@ -151,10 +176,25 @@ class MariadbUserDao @Inject()(val crewDao: MariadbCrewDao) extends UserDao {
* @param profile
* @return
*/
override def link(user: User, profile: Profile): Future[User] = {
override def link(user: User, profile: Profile): Future[Option[User]] = {
find(profile.loginInfo).flatMap(user => {
findDBUserModel(user.get.id).flatMap(userDB => {
addProfiles(userDB, List(profile))
// now insert the new profiles
// start with retrieving the crew IDs of the assigned crews
val crewIds : Future[Map[String, Option[Long]]] =
profile.supporter.crew match {
// if supporter has a crew, find the CrewDB.id by UUID crew.id
case Some(crew) => crewDao.findDBCrewModel(crew.id).map(_ match {
case Some(c) => Map(profile.loginInfo.providerKey -> Some(c.id))
case None => Map(profile.loginInfo.providerKey -> None)
})
//else None
case _ => Future.successful(Map(profile.loginInfo.providerKey -> None))
}
crewIds.flatMap(c_ids =>
dbConfig.db.run(insertProfiles(userDB.id, List(profile), c_ids))
).flatMap(_ => find(userDB.id))
//addProfiles(userDB, List(profile))
})
})
}
Expand All @@ -166,7 +206,7 @@ class MariadbUserDao @Inject()(val crewDao: MariadbCrewDao) extends UserDao {
* @param profile
* @return
*/
override def update(profile: Profile): Future[User] = {
override def update(profile: Profile): Future[Option[User]] = {
find(profile.loginInfo).flatMap(user => {
findDBUserModel(user.get.id).flatMap(userDB => {
//Delete Profile
Expand All @@ -179,9 +219,24 @@ class MariadbUserDao @Inject()(val crewDao: MariadbCrewDao) extends UserDao {
dbConfig.db.run(
(deletePasswordInfo.delete andThen deleteLoginInfo.delete andThen deleteAddress.delete andThen deleteSupporterCrew.delete andThen
deleteSupporter.delete andThen deleteProfile.delete).transactionally
).flatMap(_ =>
addProfiles(userDB, List(profile))
)
).flatMap(_ => {
// now insert the new profiles
// start with retrieving the crew IDs of the assigned crews
val crewIds: Future[Map[String, Option[Long]]] =
profile.supporter.crew match {
// if supporter has a crew, find the CrewDB.id by UUID crew.id
case Some(crew) => crewDao.findDBCrewModel(crew.id).map(_ match {
case Some(c) => Map(profile.loginInfo.providerKey -> Some(c.id))
case None => Map(profile.loginInfo.providerKey -> None)
})
//else None
case _ => Future.successful(Map(profile.loginInfo.providerKey -> None))
}
crewIds.flatMap(c_ids =>
dbConfig.db.run(insertProfiles(userDB.id, List(profile), c_ids))
).flatMap(_ => find(userDB.id))
//addProfiles(userDB, List(profile))
})
})
})
}
Expand Down Expand Up @@ -298,6 +353,78 @@ class MariadbUserDao @Inject()(val crewDao: MariadbCrewDao) extends UserDao {
dbConfig.db.run(users.filter(_.id === id).result).map(r => r.head)
}

/**
* Creates a [[DBIOAction]] that inserts a complete user as a transaction that inserts into multiple columns.
*
* @author Johann Sell
* @param userDB
* @param userProfiles
* @param crewDBids
* @return
*/
private def insertUser(userDB: UserDB, userProfiles: List[Profile], crewDBids: Map[String, Option[Long]]) : DBIOAction[Long,slick.dbio.NoStream,slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Transactional with slick.dbio.Effect.Transactional] = {
(for {
u <- (users returning users.map(_.id)) += userDB
_ <- insertProfiles(u, userProfiles, crewDBids)
} yield u).transactionally
}

/**
* Creates a [[DBIOAction]] that inserts all given profiles as a transaction and assigns them to a given users ID.
*
* @author Johann Sell
* @param userID
* @param userProfiles
* @param crewDBids
* @return
*/
private def insertProfiles(userID: Long, userProfiles: List[Profile], crewDBids: Map[String, Option[Long]]): DBIOAction[List[Long],slick.dbio.NoStream,slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Write with slick.dbio.Effect.Transactional] = {
// Assign a Supporter to an Crew via SupporterCrew. Each SupporterCrew Object contains one Role.
def supporterCrewAssignment(crewDBiD: Option[Long], s: Long, r: Option[Role]) = crewDBiD match {
case Some(crewId) => (supporterCrews returning supporterCrews.map(_.id)) += (SupporterCrewDB(s, crewId, r, None, None, None))
case _ => DBIO.successful(false)
}

// Assign Address to Supporter.
def addressAssignment(address: Option[Address], supporterId: Long) = address match {
case Some(a) => (addresses returning addresses.map(_.id)) += AddressDB(0, a, supporterId)
case _ => DBIO.successful(false)
}

DBIO.sequence(userProfiles.map(profile => (for {
// insert Profile and return long id as p
p <- (profiles returning profiles.map(_.id)) += ProfileDB(profile, userID)
// insert Supporter with profile_id = p and return long id as s
s <- (supporters returning supporters.map(_.id)) += SupporterDB(0, profile.supporter, p)
//insert Addresses with supporter_id = s.
_ <- profile.supporter.address match {
// if Addresses is a list and not Empty we insert every address in the list and return a DBIO.seq with all id's
case list if list.nonEmpty => list.tail.foldLeft(DBIO.seq(addressAssignment(list.headOption, s)))(
(seq, address) => DBIO.seq(seq, addressAssignment(Some(address), s))
)
// else return Database false
case _ => DBIO.successful(false)
}
// same as address for all roles
_ <- profile.supporter.roles match {
case list if list.nonEmpty => list.tail.foldLeft(DBIO.seq(supporterCrewAssignment(crewDBids.getOrElse(profile.loginInfo.providerKey, None), s, list.headOption)))(
(seq, role) => DBIO.seq(seq, supporterCrewAssignment(crewDBids.getOrElse(profile.loginInfo.providerKey, None), s, Some(role)))
)
case _ => DBIO.seq(supporterCrewAssignment(crewDBids.getOrElse(profile.loginInfo.providerKey, None), s, None))
}
// insert the LoginInfo PasswordInfo and OAuthInfo Models
_ <- (loginInfos returning loginInfos.map(_.id)) += LoginInfoDB(0, profile.loginInfo, p)
_ <- (profile.passwordInfo match {
case Some(passwordInfo) => (passwordInfos returning passwordInfos.map(_.id)) += PasswordInfoDB(0, passwordInfo, p)
case None => DBIO.successful(false)
})
_ <- (profile.oauth1Info match {
case Some(oAuth1Info) => (oauth1Infos returning oauth1Infos.map(_.id)) += OAuth1InfoDB(oAuth1Info, p)
case None => DBIO.successful(false)
})
} yield p).transactionally))
}

//ToDo: Export Profile DB Operations to ProfileDAO. This has to be protected, becaus it should not used outside the DAO Package
/**
* internal function to add a list of profiles for one user to the database
Expand Down
3 changes: 3 additions & 0 deletions app/models/Address.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ case class AddressStub (

) extends AddressBase {
def toAddress: Address = Address(Some(UUID.randomUUID()), street, additional, zip, city, country)

def isEmpty: Boolean =
street == "" && additional.map(_ == "").getOrElse(true) && zip == "" && city == "" && country == ""
}

/**
Expand Down
7 changes: 7 additions & 0 deletions app/models/DummyUser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ object DummyUser {
)
},
Set(),
//Set(AddressStub(
// (json \ "location" \ "street" \ "name").as[String],
// None,
// (json \ "location" \ "postcode").as[Int].toString,
// (json \ "location" \ "city").as[String],
// (json \ "location" \ "country").as[String]
//).toAddress),
Some("active"),
None
)
Expand Down
36 changes: 23 additions & 13 deletions app/services/UserService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,21 @@ class UserService @Inject() (
def retrieve(loginInfo:LoginInfo):Future[Option[User]] = userDao.find(loginInfo)
def save(user:User) = {
userDao.save(user).map(user => {
nats.publishCreate("USER", user.id)
user.map(u => {
nats.publishCreate("USER", u.id)
poolService.save(u)
})
user
})
}
//def saveImage(profile: Profile, avatar: ProfileImage) = userDao.saveProfileImage(profile, avatar)

def update(updatedUser: User) = {
userDao.replace(updatedUser).map(user => {
nats.publishUpdate("USER", updatedUser.id)
poolService.update(user) // Todo: Consider result?!
user.map(u => {
nats.publishUpdate("USER", u.id)
poolService.update(u) // Todo: Consider result?!
})
user
})
}
Expand Down Expand Up @@ -100,22 +105,27 @@ class UserService @Inject() (
}

def save(socialProfile:CommonSocialProfile) = {
def onSuccess(user: User, create: Boolean) = {
if(create) {
nats.publishCreate("USER", user.id)
poolService.save(user) // Todo: Consider result?!
} else {
nats.publishUpdate("USER", user.id)
poolService.update(user) // Todo: Consider result?!
}
def onCreate(user: Option[User]) = {
user.map(u => {
nats.publishCreate("USER", u.id)
poolService.save(u)
})
user
}

def onUpdate(user: Option[User]) = {
user.map(u => {
nats.publishUpdate("USER", u.id)
poolService.update(u)
})
user
}
val profile = Profile(socialProfile)
userDao.find(profile.loginInfo).flatMap {
case None => userDao.save(User(UUID.randomUUID(), List(profile), System.currentTimeMillis(), System.currentTimeMillis()))
.map(onSuccess(_, true))
.map(onCreate(_))
case Some(user) => userDao.update(profile)
.map(onSuccess(_, false))
.map(onUpdate(_))
}
}

Expand Down
Loading