From 60c15006f2103995e9686be74b2aa13969caeb05 Mon Sep 17 00:00:00 2001 From: Gustavo Trott Date: Wed, 23 Oct 2024 16:15:43 -0300 Subject: [PATCH] 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. --- .../core/apps/WhiteboardModel.scala | 10 ++- .../core/db/PresAnnotationDAO.scala | 65 +++----------- .../core/db/PresAnnotationHistoryDAO.scala | 89 ++++++++++++------- bbb-graphql-server/bbb_schema.sql | 16 ++-- .../tables/public_v_pres_annotation_curr.yaml | 6 +- ...public_v_pres_annotation_history_curr.yaml | 7 +- 6 files changed, 94 insertions(+), 99 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala index 9cf6fea73f..fbe51969f8 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala @@ -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 } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationDAO.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationDAO.scala index fe16e8529b..83878499d3 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationDAO.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationDAO.scala @@ -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)) ) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationHistoryDAO.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationHistoryDAO.scala index 43486eecdc..8a08367d89 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationHistoryDAO.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PresAnnotationHistoryDAO.scala @@ -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 ) } } \ No newline at end of file diff --git a/bbb-graphql-server/bbb_schema.sql b/bbb-graphql-server/bbb_schema.sql index 44b143aef4..5f8574c248 100644 --- a/bbb-graphql-server/bbb_schema.sql +++ b/bbb-graphql-server/bbb_schema.sql @@ -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; diff --git a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_curr.yaml b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_curr.yaml index 057f1c4335..22f65e3a9e 100644 --- a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_curr.yaml +++ b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_curr.yaml @@ -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: diff --git a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_history_curr.yaml b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_history_curr.yaml index 6b0031b768..dcdfa35986 100644 --- a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_history_curr.yaml +++ b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pres_annotation_history_curr.yaml @@ -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: