Re-enable the graphql type pres_annotation_history_curr to receive all annotations as incremental diff.

It will make the whiteboard more consistent and easier to merge the data.
This commit is contained in:
Gustavo Trott 2024-10-23 16:15:43 -03:00
parent c79637f21b
commit 60c15006f2
6 changed files with 94 additions and 99 deletions

View File

@ -4,7 +4,7 @@ import scala.collection.immutable.HashMap
import org.bigbluebutton.common2.msgs.AnnotationVO
import org.bigbluebutton.core.apps.whiteboard.Whiteboard
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.db.{ PresAnnotationDAO, PresPageWritersDAO }
import org.bigbluebutton.core.db.{ PresAnnotationDAO, PresAnnotationHistoryDAO, PresPageWritersDAO }
class WhiteboardModel extends SystemConfiguration {
private var _whiteboards = new HashMap[String, Whiteboard]()
@ -85,7 +85,9 @@ class WhiteboardModel extends SystemConfiguration {
}
}
PresAnnotationDAO.insertOrUpdateMap(meetingId, annotationsAdded)
val annotationUpdatedAt = System.currentTimeMillis()
PresAnnotationHistoryDAO.insertOrUpdateMap(meetingId, annotationsDiffAdded, annotationUpdatedAt)
PresAnnotationDAO.insertOrUpdateMap(meetingId, annotationsAdded, annotationUpdatedAt)
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(newWb)
@ -154,7 +156,9 @@ class WhiteboardModel extends SystemConfiguration {
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(updatedWb)
PresAnnotationDAO.delete(meetingId, userId, annotationsIdsRemoved)
val annotationUpdatedAt = System.currentTimeMillis()
PresAnnotationHistoryDAO.deleteAnnotations(meetingId, wb.id, userId, annotationsIdsRemoved, annotationUpdatedAt)
PresAnnotationDAO.deleteAnnotations(meetingId, userId, annotationsIdsRemoved, annotationUpdatedAt)
annotationsIdsRemoved
}

View File

@ -4,13 +4,12 @@ import org.bigbluebutton.common2.msgs.AnnotationVO
import slick.jdbc.PostgresProfile.api._
case class PresAnnotationDbModel(
annotationId: String,
pageId: String,
meetingId: String,
userId: String,
annotationInfo: String,
lastHistorySequence: Int,
lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
annotationId: String,
pageId: String,
meetingId: String,
userId: String,
annotationInfo: String,
lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
)
class PresAnnotationDbTableDef(tag: Tag) extends Table[PresAnnotationDbModel](tag, None, "pres_annotation") {
@ -19,38 +18,13 @@ class PresAnnotationDbTableDef(tag: Tag) extends Table[PresAnnotationDbModel](ta
val meetingId = column[String]("meetingId")
val userId = column[String]("userId")
val annotationInfo = column[String]("annotationInfo")
val lastHistorySequence = column[Int]("lastHistorySequence")
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
// def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade)
def * = (annotationId, pageId, meetingId, userId, annotationInfo, lastHistorySequence, lastUpdatedAt) <> (PresAnnotationDbModel.tupled, PresAnnotationDbModel.unapply)
def * = (annotationId, pageId, meetingId, userId, annotationInfo, lastUpdatedAt) <> (PresAnnotationDbModel.tupled, PresAnnotationDbModel.unapply)
}
object PresAnnotationDAO {
def insertOrUpdate(meetingId: String, annotation: AnnotationVO, annotationDiff: AnnotationVO) = {
// //TODO do it via trigger?
// PresAnnotationHistoryDAO.insert(meetingId, annotationDiff).onComplete {
// case Success(sequence) => {
// DatabaseConnection.logger.debug(s"Sequence generated to PresAnnotationHistory record: $sequence")
//
DatabaseConnection.enqueue(
TableQuery[PresAnnotationDbTableDef].insertOrUpdate(
PresAnnotationDbModel(
annotationId = annotation.id,
pageId = annotation.wbId,
meetingId = meetingId,
userId = annotation.userId,
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
lastHistorySequence = 0,
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis())
)
)
)
// }
// case Failure(e) => DatabaseConnection.logger.error(s"Error inserting PresAnnotationHistory: $e")
}
def prepareInsertOrUpdate(meetingId: String, annotation: AnnotationVO) = {
def prepareInsertOrUpdate(meetingId: String, annotation: AnnotationVO, annotationUpdatedAt: Long) = {
TableQuery[PresAnnotationDbTableDef].insertOrUpdate(
PresAnnotationDbModel(
annotationId = annotation.id,
@ -58,38 +32,27 @@ object PresAnnotationDAO {
meetingId = meetingId,
userId = annotation.userId,
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
lastHistorySequence = 0,
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis())
lastUpdatedAt = new java.sql.Timestamp(annotationUpdatedAt)
)
)
}
def insertOrUpdateMap(meetingId: String, annotations: Array[AnnotationVO]) = {
def insertOrUpdateMap(meetingId: String, annotations: Array[AnnotationVO], annotationUpdatedAt: Long) = {
DatabaseConnection.enqueue(
DBIO.sequence(
annotations.map { annotation =>
prepareInsertOrUpdate(meetingId, annotation)
prepareInsertOrUpdate(meetingId, annotation, annotationUpdatedAt)
}.toVector
).transactionally
)
}
def delete(wbId: String, meetingId: String, userId: String, annotationId: String) = {
// PresAnnotationHistoryDAO.delete(wbId, meetingId, userId, annotationId)
DatabaseConnection.enqueue(
TableQuery[PresAnnotationDbTableDef]
.filter(_.annotationId === annotationId)
.map(a => (a.annotationInfo, a.lastHistorySequence, a.meetingId, a.userId, a.lastUpdatedAt))
.update("", 0, meetingId, userId, new java.sql.Timestamp(System.currentTimeMillis()))
)
}
def delete(meetingId: String, userId: String, annotationIds: Array[String]) = {
def deleteAnnotations(meetingId: String, userId: String, annotationIds: Array[String], annotationUpdatedAt: Long) = {
DatabaseConnection.enqueue(
TableQuery[PresAnnotationDbTableDef]
.filter(_.annotationId inSet annotationIds)
.map(a => (a.annotationInfo, a.lastHistorySequence, a.meetingId, a.userId, a.lastUpdatedAt))
.update("", 0, meetingId, userId, new java.sql.Timestamp(System.currentTimeMillis()))
.map(a => (a.annotationInfo, a.meetingId, a.userId, a.lastUpdatedAt))
.update("", meetingId, userId, new java.sql.Timestamp(annotationUpdatedAt))
)
}

View File

@ -4,58 +4,85 @@ import org.bigbluebutton.common2.msgs.AnnotationVO
import PostgresProfile.api._
case class PresAnnotationHistoryDbModel(
sequence: Option[Int] = None,
annotationId: String,
pageId: String,
meetingId: String,
userId: String,
annotationInfo: String
// lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
annotationInfo: String,
updatedAt: java.sql.Timestamp
)
class PresAnnotationHistoryDbTableDef(tag: Tag) extends Table[PresAnnotationHistoryDbModel](tag, None, "pres_annotation_history") {
val sequence = column[Option[Int]]("sequence", O.PrimaryKey, O.AutoInc)
val annotationId = column[String]("annotationId")
val pageId = column[String]("pageId")
val meetingId = column[String]("meetingId")
val userId = column[String]("userId")
val annotationInfo = column[String]("annotationInfo")
// val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
// def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade)
def * = (sequence, annotationId, pageId, meetingId, userId, annotationInfo) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply)
val updatedAt = column[java.sql.Timestamp]("updatedAt")
def * = (annotationId, pageId, meetingId, userId, annotationInfo, updatedAt) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply)
}
object PresAnnotationHistoryDAO {
def insert(meetingId: String, annotationDiff: AnnotationVO) = {
DatabaseConnection.db.run(
//TODO not being used for now
TableQuery[PresAnnotationHistoryDbTableDef].returning(
TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence)
) += PresAnnotationHistoryDbModel(
None,
annotationId = annotationDiff.id,
pageId = annotationDiff.wbId,
meetingId = meetingId,
userId = annotationDiff.userId,
annotationInfo = JsonUtils.mapToJson(annotationDiff.annotationInfo).compactPrint
)
)
}
def delete(wbId: String, meetingId: String, userId: String, annotationId: String) = {
DatabaseConnection.db.run(
//TODO not being used for now
TableQuery[PresAnnotationHistoryDbTableDef].returning(
TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence)
) += PresAnnotationHistoryDbModel(
None,
def delete(wbId: String, meetingId: String, userId: String, annotationId: String, annotationUpdatedAt: Long) = {
DatabaseConnection.enqueue(
TableQuery[PresAnnotationHistoryDbTableDef].forceInsert(
PresAnnotationHistoryDbModel(
// None,
annotationId = annotationId,
pageId = wbId,
meetingId = meetingId,
userId = userId,
annotationInfo = ""
annotationInfo = "",
updatedAt = new java.sql.Timestamp(annotationUpdatedAt)
)
)
)
}
def prepareInsertOrUpdate(meetingId: String, annotation: AnnotationVO, annotationUpdatedAt: Long) = {
TableQuery[PresAnnotationHistoryDbTableDef].forceInsert(
PresAnnotationHistoryDbModel(
annotationId = annotation.id,
pageId = annotation.wbId,
meetingId = meetingId,
userId = annotation.userId,
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
updatedAt = new java.sql.Timestamp(annotationUpdatedAt)
)
)
}
def insertOrUpdateMap(meetingId: String, annotations: Array[AnnotationVO], annotationUpdatedAt: Long) = {
DatabaseConnection.enqueue(
DBIO.sequence(
annotations.map { annotation =>
prepareInsertOrUpdate(meetingId, annotation, annotationUpdatedAt)
}.toVector
).transactionally
)
}
def prepareDelete(meetingId: String, pageId: String, annotationId: String, userId: String, annotationUpdatedAt: Long) = {
TableQuery[PresAnnotationHistoryDbTableDef].forceInsert(
PresAnnotationHistoryDbModel(
annotationId = annotationId,
pageId = pageId,
meetingId = meetingId,
userId = userId,
annotationInfo = "",
updatedAt = new java.sql.Timestamp(annotationUpdatedAt)
)
)
}
def deleteAnnotations(meetingId: String, pageId: String, userId: String, annotations: Array[String], annotationUpdatedAt: Long) = {
DatabaseConnection.enqueue(
DBIO.sequence(
annotations.map { annotationId =>
prepareDelete(meetingId, pageId, annotationId, userId, annotationUpdatedAt)
}.toVector
).transactionally
)
}
}

View File

@ -1366,8 +1366,7 @@ CREATE TABLE "pres_annotation" (
"meetingId" varchar(100),
"userId" varchar(50),
"annotationInfo" TEXT,
"lastHistorySequence" integer,
"lastUpdatedAt" timestamp with time zone DEFAULT now()
"lastUpdatedAt" timestamp with time zone
);
CREATE INDEX "idx_pres_annotation_pageId" ON "pres_annotation"("pageId");
CREATE INDEX "idx_pres_annotation_updatedAt" ON "pres_annotation"("pageId","lastUpdatedAt");
@ -1379,25 +1378,30 @@ CREATE TABLE "pres_annotation_history" (
"pageId" varchar(100) REFERENCES "pres_page"("pageId") ON DELETE CASCADE,
"meetingId" varchar(100),
"userId" varchar(50),
"annotationInfo" TEXT
-- "lastUpdatedAt" timestamp with time zone DEFAULT now()
"annotationInfo" TEXT,
"updatedAt" timestamp with time zone
);
CREATE INDEX "idx_pres_annotation_history_pageId" ON "pres_annotation"("pageId");
create index "idx_pres_annotation_history_user_meeting" on "pres_annotation_history" ("userId", "meetingId");
CREATE INDEX "idx_pres_annotation_history_updatedAt" ON "pres_annotation_history"("pageId", "updatedAt");
CREATE VIEW "v_pres_annotation_curr" AS
SELECT p."meetingId", pp."presentationId", pa."annotationId", pa."pageId", pa."userId", pa."annotationInfo", pa."lastHistorySequence", pa."lastUpdatedAt"
SELECT p."meetingId", pp."presentationId", pa."annotationId", pa."pageId", pa."userId", pa."annotationInfo",
pa."lastUpdatedAt", "user"."isModerator" as "userIsModerator"
FROM pres_presentation p
JOIN pres_page pp ON pp."presentationId" = p."presentationId"
JOIN pres_annotation pa ON pa."pageId" = pp."pageId"
JOIN "user" on "user"."meetingId" = pa."meetingId" and "user"."userId" = pa."userId"
WHERE p."current" IS true
AND pp."current" IS true;
CREATE VIEW "v_pres_annotation_history_curr" AS
SELECT p."meetingId", pp."presentationId", pah."pageId", pah."userId", pah."annotationId", pah."annotationInfo", pah."sequence"
SELECT p."meetingId", pp."presentationId", pah."pageId", pah."userId", pah."annotationId", pah."annotationInfo",
pah."updatedAt", "user"."isModerator" as "userIsModerator"
FROM pres_presentation p
JOIN pres_page pp ON pp."presentationId" = p."presentationId"
JOIN pres_annotation_history pah ON pah."pageId" = pp."pageId"
JOIN "user" on "user"."meetingId" = pah."meetingId" and "user"."userId" = pah."userId"
WHERE p."current" IS true
AND pp."current" IS true;

View File

@ -24,7 +24,6 @@ select_permissions:
- pageId
- presentationId
- userId
- lastHistorySequence
- annotationInfo
- lastUpdatedAt
filter:
@ -32,9 +31,8 @@ select_permissions:
- meetingId:
_eq: X-Hasura-MeetingId
- _or:
- user:
isModerator:
_eq: true
- userIsModerator:
_eq: true
- meetingId:
_eq: X-Hasura-AnnotationsNotLockedInMeeting
- userId:

View File

@ -24,16 +24,15 @@ select_permissions:
- pageId
- presentationId
- userId
- sequence
- updatedAt
- annotationInfo
filter:
_and:
- meetingId:
_eq: X-Hasura-MeetingId
- _or:
- user:
isModerator:
_eq: true
- userIsModerator:
_eq: true
- meetingId:
_eq: X-Hasura-AnnotationsNotLockedInMeeting
- userId: