All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
sss.openstar.contacts.ContactService.scala Maven / Gradle / Ivy
package sss.openstar.contacts
import java.sql.SQLIntegrityConstraintViolationException
import akka.util.ByteString
import sss.db._
import sss.openstar.UniqueNodeIdentifier
import sss.openstar.contacts.ContactService.{CategoryDetails, Contact, LoggedInUser}
import sss.openstar.schemamigration.SqlSchemaNames.ColumnNames._
import sss.db.ops.DbOps.{DbRunOps, FutureTxOps}
import sss.openstar.schemamigration.SqlSchemaNames
object ContactService {
trait User {
val name: UniqueNodeIdentifier
val avatar: Option[ByteString]
def setAvatar(avatar: Option[ByteString]): Unit
}
case class LoggedInUser(private [contacts] val id: Long,
name: UniqueNodeIdentifier,
avatar: Option[ByteString]
)(implicit private [contacts] val contactService: ContactService) extends User {
def setAvatar(avatar: Option[ByteString]): Unit =
contactService.updateAvatar(id, avatar)
def update(updatedAvatar: Option[ByteString]): LoggedInUser = {
setAvatar(updatedAvatar)
copy(avatar = updatedAvatar)
}
def getCategoryDetails(contactName: UniqueNodeIdentifier): Option[CategoryDetails] = {
require(contactName != name, s"Cant get category details with yourself $contactName")
contactService.getCategoryDetails(id, contactName)
}
def setCategoryDetails(invitee: UniqueNodeIdentifier,
invitorCategory: String,
inviteeCategory: String,
inviteeAvatar: Option[ByteString]): Unit = {
require(invitee != name, s"Cant set category details with yourself $invitee")
contactService
.setCategoryDetails(
id,
invitee,
invitorCategory,
inviteeCategory,
inviteeAvatar
)
}
def contacts: Seq[Contact] = contactService.contacts(id)
}
case class Contact(
private [contacts] val id: Long,
name: UniqueNodeIdentifier,
avatar: Option[ByteString]
)(implicit private [contacts] val contactService: ContactService) extends User {
def setAvatar(avatar: Option[ByteString]): Unit =
contactService.updateAvatar(id, avatar)
}
case class CategoryDetails(
nameCategoryForContact: String,
contact: UniqueNodeIdentifier,
contactCategoryForName: String
)
}
class ContactService(implicit db:Db) {
private implicit val contactService: ContactService = this
lazy private val contactTable = db.table(SqlSchemaNames.TableNames.contactTableName)
lazy private val contactCategoriesTable = db.table(SqlSchemaNames.TableNames.contactCategoriesTableName)
def addContact(userId: UniqueNodeIdentifier, avatar: Option[ByteString] = None): Contact = addContactFTx(userId, avatar).dbRunSyncGet
def addContactFTx(userId: UniqueNodeIdentifier, avatar: Option[ByteString] = None): FutureTx[Contact] = {
contactTable.insert(Map(
contactCol -> userId,
avatarCol -> avatar.map(_.toArray)
)).map(rowToContact)
.recoverWithDb {
case e: SQLIntegrityConstraintViolationException =>
for {
rowOpt <- contactTable
.getRow(
where(
contactCol -> userId
))
updated <- contactTable.updateRow(rowOpt.get + (avatarCol -> avatar.map(_.toArray)))
} yield rowToContact(updated)
}
}
private [contacts] def updateAvatar(contactId: Long, avatar: Option[ByteString]): Contact = {
contactTable
.updateRow(
Map(
avatarCol -> avatar.map(_.toArray),
idCol -> contactId
)
).map(rowToContact)
}.dbRunSyncGet
private [contacts] def getContact(contactId: Long): Contact = rowToContact(contactTable(contactId).dbRunSyncGet)
private def rowToContact(row: Row): Contact =
Contact(
row.id,
row.string(contactCol),
row.arrayByteOpt(avatarCol)
.map(ByteString.apply)
)
private def findRowId(contact: UniqueNodeIdentifier): FutureTx[Option[Long]] = findRow(contact).map(_.map(_.id))
private def findRow(contact: UniqueNodeIdentifier): FutureTx[Option[Row]] =
contactTable
.find(
where(contactCol -> contact)
)
def findUser(loggedInUser: UniqueNodeIdentifier): Option[LoggedInUser] = {
findRow(loggedInUser).dbRunSyncGet.map { row =>
LoggedInUser(row.id,
row.string(contactCol),
row.arrayByteOpt(avatarCol)
.map(ByteString.apply))
}
}
def findContact(contact: UniqueNodeIdentifier): Option[Contact] =
findRow(contact).dbRunSyncGet
.map(rowToContact)
private [contacts] def getCategoryDetails(
meId: Long,
contact: UniqueNodeIdentifier): Option[CategoryDetails] = {
for {
contactId <- findRowId(contact)
rowOpt <- contactCategoriesTable.find(where(localContactLnkCol -> meId, remoteContactLnkCol -> contactId))
} yield rowOpt.map(row => CategoryDetails(row.string(localCategoryCol), contact, row.string(remoteCategoryCol)))
}.dbRunSyncGet
private [contacts] def setCategoryDetails(meId: Long,
invitee: UniqueNodeIdentifier,
invitorCategory: String,
inviteeCategory: String,
inviteeAvatar: Option[ByteString] = None): Unit = {
val dbPlan = for {
getContact <- addContactFTx(invitee, inviteeAvatar)
newValues = Map(
localContactLnkCol -> meId,
localCategoryCol -> invitorCategory,
remoteContactLnkCol -> getContact.id,
remoteCategoryCol -> inviteeCategory
)
foundOpt <- contactCategoriesTable.find(
where(
localContactLnkCol -> meId,
remoteContactLnkCol -> getContact.id)
)
_ <- foundOpt match {
case Some(row) =>
contactCategoriesTable.updateRow(row ++ newValues)
case None =>
contactCategoriesTable.insert(newValues)
}
} yield ()
dbPlan.dbRunSyncGet
}
private[contacts] def contacts(meId: Long): Seq[Contact] = (for {
categoriesRows <- contactCategoriesTable.filter(
where(
localContactLnkCol -> meId
)
)
contacts <- FutureTx.sequence {
for {
row <- categoriesRows
id = row.long(remoteContactLnkCol)
} yield (contactTable(id))
}
} yield (contacts.map(rowToContact))).dbRunSyncGet
}