Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/bootstrap/liftweb/Boot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ class Boot extends MdcLoggable {

def schemifyAll() = {
Schemifier.schemify(true, Schemifier.infoF _, ToSchemify.models: _*)
// Create default system-level "general" chat room (all_users_are_participants = true)
// Create default system-level "general" chat room (is_open_room = true)
code.chat.ChatRoomTrait.chatRoomProvider.vend.getOrCreateDefaultRoom()
}

Expand Down
8 changes: 4 additions & 4 deletions obp-api/src/main/scala/code/api/util/ApiRole.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1379,10 +1379,10 @@ object ApiRole extends MdcLoggable{
lazy val canArchiveBankChatRoom = CanArchiveBankChatRoom()
case class CanArchiveSystemChatRoom(requiresBankId: Boolean = false) extends ApiRole
lazy val canArchiveSystemChatRoom = CanArchiveSystemChatRoom()
case class CanSetBankChatRoomAUAP(requiresBankId: Boolean = true) extends ApiRole
lazy val canSetBankChatRoomAUAP = CanSetBankChatRoomAUAP()
case class CanSetSystemChatRoomAUAP(requiresBankId: Boolean = false) extends ApiRole
lazy val canSetSystemChatRoomAUAP = CanSetSystemChatRoomAUAP()
case class CanSetBankChatRoomIsOpenRoom(requiresBankId: Boolean = true) extends ApiRole
lazy val canSetBankChatRoomIsOpenRoom = CanSetBankChatRoomIsOpenRoom()
case class CanSetSystemChatRoomIsOpenRoom(requiresBankId: Boolean = false) extends ApiRole
lazy val canSetSystemChatRoomIsOpenRoom = CanSetSystemChatRoomIsOpenRoom()

private val dynamicApiRoles = new ConcurrentHashMap[String, ApiRole]

Expand Down
17 changes: 13 additions & 4 deletions obp-api/src/main/scala/code/api/util/Glossary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5140,13 +5140,13 @@ object Glossary extends MdcLoggable {
|### Chat Rooms
|A Chat Room is a named space where participants exchange messages.
|
|A system-level room called **general** is created automatically at startup with **all_users_are_participants = true** — meaning every authenticated user can read and send messages without needing an explicit Participant record.
|A system-level room called **general** is created automatically at startup with **is_open_room = true** — meaning every authenticated user can read and send messages without needing an explicit Participant record.
|
|Each room has:
|- A unique **joining key** (UUID) that can be shared to invite others. The key can be refreshed to revoke access.
|- A **name** that is unique within its scope (per bank, or globally for system-level rooms).
|- An optional **bank_id** — if set, the room is scoped to that bank. If empty, it is a system-level room.
|- An **all_users_are_participants** flag — if true, all authenticated users are treated as implicit participants without needing a database record. They can read and send messages but have no special permissions.
|- An **is_open_room** flag — if true, all authenticated users are treated as implicit participants without needing a database record. They can read and send messages but have no special permissions.
|
|### Participants
|A Participant is a user or consumer (application/bot) that belongs to a Chat Room. Participants can:
Expand Down Expand Up @@ -5192,13 +5192,22 @@ object Glossary extends MdcLoggable {
|### Polling
|Clients retrieve new messages by polling the GET messages endpoint with a **since** parameter (timestamp). This avoids the complexity of WebSocket infrastructure while providing a simple, reliable mechanism for near-real-time updates.
|
|### gRPC Streaming (real-time)
|For clients that need true real-time updates without polling, OBP exposes a **ChatStreamService** over gRPC (see `chat.proto`, package `code.obp.grpc.chat.g1`). It provides four server-streaming / bidirectional RPCs:
|- **StreamMessages(StreamMessagesRequest) → stream ChatMessageEvent** — push new/edited/deleted messages for a given chat room as they happen.
|- **StreamTyping(stream TypingEvent) → stream TypingIndicator** — bidirectional stream: clients send their own typing state, server fans out typing indicators from other participants.
|- **StreamPresence(StreamPresenceRequest) → stream PresenceEvent** — online/offline updates for participants in a room.
|- **StreamUnreadCounts(StreamUnreadCountsRequest) → stream UnreadCountEvent** — per-room unread counters for the authenticated user.
|
|gRPC calls are authenticated via the same credentials as REST (see `AuthInterceptor`). The REST polling endpoints remain the canonical API; the gRPC streams are an optional push channel for clients that want lower latency and less request overhead.
|
|## API Endpoints
|
|All chat endpoints are available in two forms:
|All chat REST endpoints are available in two forms:
|- **Bank-scoped**: /banks/BANK_ID/chat-rooms/...
|- **System-level**: /chat-rooms/...
|
|See the API Explorer for the full list of Chat endpoints, tagged with **Chat**.
|See the API Explorer for the full list of Chat endpoints, tagged with **Chat**. For the real-time streaming surface, see `chat.proto` / `ChatStreamServiceImpl`.
|
""")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ object Migration extends MdcLoggable {
updateConsentViewAddJwtPayload(startedBeforeSchemifier)
updateConsentViewAddJwtExpiresAt(startedBeforeSchemifier)
updateAccountAccessWithViewsViewUnionAll(startedBeforeSchemifier)
migrateChatRoomIsOpenRoom()
}

private def dummyScript(): Boolean = {
Expand Down Expand Up @@ -649,6 +650,13 @@ object Migration extends MdcLoggable {
}
}
}

private def migrateChatRoomIsOpenRoom(): Boolean = {
val name = nameOf(migrateChatRoomIsOpenRoom)
runOnce(name) {
MigrationOfChatRoomIsOpenRoom.migrateColumn(name)
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package code.api.util.migration

import code.api.util.APIUtil
import code.api.util.migration.Migration.{DbFunction, saveLog}
import code.chat.ChatRoom
import net.liftweb.common.Full
import net.liftweb.db.DB
import net.liftweb.mapper.Schemifier
import net.liftweb.util.DefaultConnectionIdentifier

object MigrationOfChatRoomIsOpenRoom {

/**
* Migrate the old `allusersareparticipants` column to the new `isopenroom` column.
*
* Schemifier will have already created the new `isopenroom` column (defaulting to false).
* This migration copies data from the old column and then drops it.
*
* If the old column does not exist (fresh install), this is a no-op.
*/
def migrateColumn(name: String): Boolean = {
DbFunction.tableExists(ChatRoom) match {
case true =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit

// Check if the old column exists before attempting migration
val oldColumnExists = try {
DB.use(DefaultConnectionIdentifier) { conn =>
val rs = conn.getMetaData.getColumns(null, null, "chatroom", "allusersareparticipants")
val exists = rs.next()
rs.close()
exists
}
} catch {
case _: Throwable => false
}

if (!oldColumnExists) {
val endDate = System.currentTimeMillis()
val comment = "Old column allusersareparticipants does not exist (fresh install). No migration needed."
saveLog(name, commitId, true, startDate, endDate, comment)
return true
}

var isSuccessful = false

val executedSql =
DbFunction.maybeWrite(true, Schemifier.infoF _) {
APIUtil.getPropsValue("db.driver") match {
case Full(dbDriver) if dbDriver.contains("com.microsoft.sqlserver.jdbc.SQLServerDriver") =>
() =>
"""
|UPDATE chatroom SET isopenroom = allusersareparticipants WHERE allusersareparticipants IS NOT NULL;
|ALTER TABLE chatroom DROP COLUMN allusersareparticipants;
|""".stripMargin
case _ =>
// PostgreSQL and MySQL
() =>
"""
|UPDATE chatroom SET isopenroom = allusersareparticipants WHERE allusersareparticipants IS NOT NULL;
|ALTER TABLE chatroom DROP COLUMN allusersareparticipants;
|""".stripMargin
}

Check warning on line 64 in obp-api/src/main/scala/code/api/util/migration/MigrationOfChatRoomIsOpenRoom.scala

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this conditional structure or edit its code blocks so that they're not all the same.

See more on https://sonarcloud.io/project/issues?id=OpenBankProject_OBP-API&issues=AZ1nnN-xb2zIfKUB6ZeD&open=AZ1nnN-xb2zIfKUB6ZeD&pullRequest=2751
}

val endDate = System.currentTimeMillis()
val comment: String =
s"""Executed SQL:
|$executedSql
|""".stripMargin
isSuccessful = true
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful

case false =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
val isSuccessful = false
val endDate = System.currentTimeMillis()
val comment: String =
s"""${ChatRoom._dbTableNameLC} table does not exist"""
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful
}
}
}
Loading
Loading