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.message.MessageInBox.scala Maven / Gradle / Ivy
package sss.openstar.message
import java.sql.SQLIntegrityConstraintViolationException
import java.time.LocalDateTime
import sss.ancillary.{Guid, Logging}
import sss.db._
import sss.openstar.UniqueNodeIdentifier
import sss.openstar.schemamigration.SqlSchemaNames.ColumnNames._
import sss.openstar.message.MessageInBox.MessageQueryType.MessageQueryType
import sss.openstar.util.DateOps._
import sss.db.ops.DbOps.{DbRunOps, OptFutureTxOps}
import sss.openstar.schemamigration.SqlSchemaNames.TableNames.messageInboxTableName
object MessageInBox {
object MessageQueryType extends Enumeration {
type MessageQueryType = MessageQuery
val NewestFirst = Value("NewestFirst", where(s"$parentGuidCol IS null") orderBy(OrderDesc(updatedAtCol)))
val DefaultOrder = Value("DefaultOrder", where(s"$parentGuidCol IS null"))
val DefaultOrderAll = Value("DefaultOrder", where())
class MessageQuery private[MessageQueryType](name: String, val where : Where) extends Val(nextId, name)
protected final def Value(name: String, where : Where): MessageQuery = new MessageQuery(name, where)
}
trait SavedMessage {
val index: Long
val msg: Message
val savedAt: LocalDateTime
val owner: UniqueNodeIdentifier
}
object SavedMessage {
def apply(
indx: Long,
mesg: Message,
svedAt: LocalDateTime,
ownr: UniqueNodeIdentifier
): SavedMessage = new SavedMessage {
override val index: Long = indx
override val msg: Message = mesg
override val savedAt: LocalDateTime = svedAt
override val owner: UniqueNodeIdentifier = ownr
}
}
object MsgStatus extends Enumeration {
type MsgStatus = Value
val New = Value(0)
val Archived = Value(1)
val SentConfirmed = Value(2)
val Deleted = Value(3)
val Junk = Value(4)
}
private var messageInBoxTable: Table = _
private val messageInBoxTableLock = new Object()
def apply(identity: UniqueNodeIdentifier)(implicit db: Db): MessageInBox = {
if (messageInBoxTable eq null) {
messageInBoxTableLock.synchronized {
if (messageInBoxTable eq null) {
messageInBoxTable = createTable
}
}
}
new MessageInBox(identity, messageInBoxTable)
}
class MessagePage[M](page: FutureTx[Option[Page]], f: Row => M)(implicit db:Db) {
lazy val hasNext: Boolean = page.flatMap(_.map(_.hasNext).toFutureTxOpt).dbRunSyncGet.contains(true)
lazy val hasPrev: Boolean = page.flatMap(_.map(_.hasPrev).toFutureTxOpt).dbRunSyncGet.contains(true)
val messages: Seq[M] = page.dbRunSyncGet.map(_.rows map f).getOrElse(Seq.empty)
lazy val next: MessagePage[M] = new MessagePage[M](page.flatMap(_.map(_.next).toFutureTxOpt).map(_.flatten), f)
lazy val prev: MessagePage[M] = new MessagePage[M](page.flatMap(_.map(_.prev).toFutureTxOpt).map(_.flatten), f)
}
private def createTable(implicit db: Db): Table = db.table(messageInboxTableName )
}
class MessageInBox(
val owner: UniqueNodeIdentifier,
private val table: Table
)(implicit db: Db) extends Logging {
import MessageInBox.MsgStatus._
import MessageInBox._
private def toMsg(r: Row): Message = Message(
r.arrayByte(messageCol).toMessagePayload,
Guid(r.arrayByte(guidCol)),
r.arrayByteOpt(parentGuidCol) map Guid.apply
)
private def toSavedMessage(r: Row): SavedMessage = SavedMessage(
r.long(idCol),
toMsg(r),
r.long(updatedAtCol).toLocalDateTime,
r.string(ownerCol)
)
def guidToId(guid: Guid): Long = {
table
.find(where(ownerCol -> owner, guidCol -> guid.value))
.map(_.map(_.id).getOrElse(throw new IllegalArgumentException(s"Guid $guid not in message inbox of $owner")))
.dbRunSyncGet
}
def idToGuid(id: Long): Option[Guid] = {
table
.find(where(ownerCol -> owner, idCol -> id))
.map(_.map(r => Guid(r.arrayByte(guidCol))))
.dbRunSyncGet
}
def find(guid: Guid): Option[SavedMessage] = {
findFTx(guid).dbRunSyncGet
}
def findFTx(guid: Guid): FutureTx[Option[SavedMessage]] = {
table
.find(where(ownerCol -> owner, guidCol -> guid.value))
.map(_.map(toSavedMessage))
}
def get(id: Long): SavedMessage = {
table
.find(where(idCol -> id))
.map(_.map(toSavedMessage).getOrElse(throw new IllegalArgumentException(s"No suchrow for id $id")))
.dbRunSyncGet
}
def update(msg: Message): Unit = {
table
.update(
Map(
messageCol -> msg.msgPayload.toBytes,
updatedAtCol -> LocalDateTime.now().toMillis
),
where(ownerCol -> owner, guidCol -> msg.guid.value)
)
.dbRunSyncGet
}
def page(
offset: Long,
limit: Int,
query: MessageQueryType
): Seq[SavedMessage] = {
table
.filter(
query
.where
.and(where(ownerCol -> owner))
.limit(offset, limit)
)
.map(_.map(toSavedMessage))
.dbRunSyncGet
}
def count(q: MessageQueryType): Long = {
table
.filter(q.where.and(where(ownerCol -> owner)))
.dbRunSyncGet
.size
}
def addNew(msg: Message): Either[SavedMessage,SavedMessage] = add(msg, New)
def addJunk(msg: Message): Either[SavedMessage,SavedMessage] = add(msg, Junk)
private def add(msg: Message, status: MsgStatus): Either[SavedMessage, SavedMessage] = {
val bs = msg.msgPayload.toBytes
val mp = Map(
ownerCol -> owner,
statusCol -> status,
messageCol -> bs,
guidCol -> msg.guid.value,
parentGuidCol -> msg.parentGuid.map(_.value),
updatedAtCol -> LocalDateTime.now().toMillis
)
table
.insert(mp)
.dbRunSync
.map(toSavedMessage)
.map(Right(_))
.recover {
case e: SQLIntegrityConstraintViolationException =>
find(msg.guid).map(Left(_)).getOrElse(throw e)
}
.get
}
def inBoxPager(pageSize: Int): MessagePage[SavedMessage] =
new MessagePage(
PagedView(
table,
pageSize,
where(ownerCol -> owner, statusCol -> New) orderDesc updatedAtCol,
).lastPage,
toSavedMessage
)
def archivedPager(pageSize: Int): MessagePage[SavedMessage] =
new MessagePage(
PagedView(
table,
pageSize,
where(ownerCol -> owner, statusCol -> Archived) orderDesc updatedAtCol,
).lastPage,
toSavedMessage
)
def junkPager(pageSize: Int): MessagePage[SavedMessage] =
new MessagePage(
PagedView(
table,
pageSize,
where(ownerCol -> owner, statusCol -> Junk) orderDesc updatedAtCol,
).lastPage,
toSavedMessage
)
def archive(saved: SavedMessage): Unit = {
table
.update(
Map(statusCol -> Archived),
where(idCol -> saved.index)
)
.dbRunSyncGet
}
def deleteSent(saved: SavedMessage): Unit = {
table
.delete(where(idCol -> saved.index))
.dbRunSyncGet
}
def delete(guid: Guid): Boolean = {
val numDeleted = table
.delete(where(ownerCol -> owner, guidCol -> guid.value))
.dbRunSyncGet
require(numDeleted == 1 || numDeleted == 0, s"More than 1 guid deleted? $numDeleted $guid")
numDeleted == 1
}
def delete(saved: SavedMessage): Boolean = {
val numDeleted = table
.delete(where(idCol -> saved.index))
.dbRunSyncGet
require(numDeleted == 1 || numDeleted == 0, s"More than 1 message deleted? $numDeleted $saved")
numDeleted == 1
}
}