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.common2.msgs.AnnotationVO
import org.bigbluebutton.core.apps.whiteboard.Whiteboard import org.bigbluebutton.core.apps.whiteboard.Whiteboard
import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.db.{ PresAnnotationDAO, PresPageWritersDAO } import org.bigbluebutton.core.db.{ PresAnnotationDAO, PresAnnotationHistoryDAO, PresPageWritersDAO }
class WhiteboardModel extends SystemConfiguration { class WhiteboardModel extends SystemConfiguration {
private var _whiteboards = new HashMap[String, Whiteboard]() 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) val newWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(newWb) saveWhiteboard(newWb)
@ -154,7 +156,9 @@ class WhiteboardModel extends SystemConfiguration {
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap) val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(updatedWb) 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 annotationsIdsRemoved
} }

View File

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

View File

@ -4,58 +4,85 @@ import org.bigbluebutton.common2.msgs.AnnotationVO
import PostgresProfile.api._ import PostgresProfile.api._
case class PresAnnotationHistoryDbModel( case class PresAnnotationHistoryDbModel(
sequence: Option[Int] = None,
annotationId: String, annotationId: String,
pageId: String, pageId: String,
meetingId: String, meetingId: String,
userId: String, userId: String,
annotationInfo: String annotationInfo: String,
// lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis()) updatedAt: java.sql.Timestamp
) )
class PresAnnotationHistoryDbTableDef(tag: Tag) extends Table[PresAnnotationHistoryDbModel](tag, None, "pres_annotation_history") { 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 annotationId = column[String]("annotationId")
val pageId = column[String]("pageId") val pageId = column[String]("pageId")
val meetingId = column[String]("meetingId") val meetingId = column[String]("meetingId")
val userId = column[String]("userId") val userId = column[String]("userId")
val annotationInfo = column[String]("annotationInfo") val annotationInfo = column[String]("annotationInfo")
// val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt") val updatedAt = column[java.sql.Timestamp]("updatedAt")
// def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade) def * = (annotationId, pageId, meetingId, userId, annotationInfo, updatedAt) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply)
def * = (sequence, annotationId, pageId, meetingId, userId, annotationInfo) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply)
} }
object PresAnnotationHistoryDAO { object PresAnnotationHistoryDAO {
def insert(meetingId: String, annotationDiff: AnnotationVO) = { def delete(wbId: String, meetingId: String, userId: String, annotationId: String, annotationUpdatedAt: Long) = {
DatabaseConnection.db.run( DatabaseConnection.enqueue(
//TODO not being used for now TableQuery[PresAnnotationHistoryDbTableDef].forceInsert(
TableQuery[PresAnnotationHistoryDbTableDef].returning( PresAnnotationHistoryDbModel(
TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence) // None,
) += 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,
annotationId = annotationId, annotationId = annotationId,
pageId = wbId, pageId = wbId,
meetingId = meetingId, meetingId = meetingId,
userId = userId, 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), "meetingId" varchar(100),
"userId" varchar(50), "userId" varchar(50),
"annotationInfo" TEXT, "annotationInfo" TEXT,
"lastHistorySequence" integer, "lastUpdatedAt" timestamp with time zone
"lastUpdatedAt" timestamp with time zone DEFAULT now()
); );
CREATE INDEX "idx_pres_annotation_pageId" ON "pres_annotation"("pageId"); CREATE INDEX "idx_pres_annotation_pageId" ON "pres_annotation"("pageId");
CREATE INDEX "idx_pres_annotation_updatedAt" ON "pres_annotation"("pageId","lastUpdatedAt"); 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, "pageId" varchar(100) REFERENCES "pres_page"("pageId") ON DELETE CASCADE,
"meetingId" varchar(100), "meetingId" varchar(100),
"userId" varchar(50), "userId" varchar(50),
"annotationInfo" TEXT "annotationInfo" TEXT,
-- "lastUpdatedAt" timestamp with time zone DEFAULT now() "updatedAt" timestamp with time zone
); );
CREATE INDEX "idx_pres_annotation_history_pageId" ON "pres_annotation"("pageId"); 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_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 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 FROM pres_presentation p
JOIN pres_page pp ON pp."presentationId" = p."presentationId" JOIN pres_page pp ON pp."presentationId" = p."presentationId"
JOIN pres_annotation pa ON pa."pageId" = pp."pageId" 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 WHERE p."current" IS true
AND pp."current" IS true; AND pp."current" IS true;
CREATE VIEW "v_pres_annotation_history_curr" AS 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 FROM pres_presentation p
JOIN pres_page pp ON pp."presentationId" = p."presentationId" JOIN pres_page pp ON pp."presentationId" = p."presentationId"
JOIN pres_annotation_history pah ON pah."pageId" = pp."pageId" 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 WHERE p."current" IS true
AND pp."current" IS true; AND pp."current" IS true;

View File

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

View File

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