Skip to content

Commit c7360c3

Browse files
committed
Chat Room search (POST)
1 parent bba090d commit c7360c3

4 files changed

Lines changed: 140 additions & 0 deletions

File tree

obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16351,6 +16351,89 @@ trait APIMethods600 {
1635116351
}
1635216352
}
1635316353

16354+
// 25b. searchChatRooms
16355+
staticResourceDocs += ResourceDoc(
16356+
searchChatRooms,
16357+
implementedInApiVersion,
16358+
nameOf(searchChatRooms),
16359+
"POST",
16360+
"/chat-rooms/search",
16361+
"Search Chat Rooms",
16362+
s"""Search chat rooms the current user is a participant of, filtered by the supplied criteria.
16363+
|
16364+
|Currently supports filtering by participant set:
16365+
|
16366+
|- `with_user_ids` (array of user_id strings, required): only return rooms where the current user
16367+
| AND every listed user_id are participants. Pass an empty list to match all of the current user's rooms.
16368+
|- `exact_participants` (boolean, optional, default `false`): if `true`, the room's participant set
16369+
| must equal exactly `{current user} ∪ with_user_ids` with no extras. Open rooms are excluded
16370+
| from exact-participant searches because their participant set is implicitly "everyone".
16371+
|
16372+
|Primary use case: a client looking up an existing 1-on-1 direct-message room before creating one,
16373+
|by calling with `with_user_ids: [<other user_id>]` and `exact_participants: true`.
16374+
|
16375+
|The response shape is the same as `Get My Chat Rooms`.
16376+
|
16377+
|Authentication is Required
16378+
|
16379+
|""".stripMargin,
16380+
ChatRoomSearchRequestJsonV600(
16381+
with_user_ids = List("user-id-123"),
16382+
exact_participants = Some(true)
16383+
),
16384+
ChatRoomsJsonV600(chat_rooms = List(ChatRoomJsonV600(
16385+
chat_room_id = "chat-room-id-123",
16386+
bank_id = "",
16387+
name = "DM with robert.x.0.gh",
16388+
description = "",
16389+
joining_key = "abc123key",
16390+
created_by = "user-id-456",
16391+
created_by_username = "alice",
16392+
created_by_provider = "https://github.com",
16393+
is_open_room = false,
16394+
is_archived = false,
16395+
last_message_at = Some(new java.util.Date()),
16396+
last_message_preview = Some("Hello!"),
16397+
last_message_sender = Some("alice"),
16398+
unread_count = Some(0),
16399+
created_at = new java.util.Date(),
16400+
updated_at = new java.util.Date()
16401+
))),
16402+
List(
16403+
$AuthenticatedUserIsRequired,
16404+
InvalidJsonFormat,
16405+
UnknownError
16406+
),
16407+
List(apiTagChat),
16408+
None
16409+
)
16410+
16411+
lazy val searchChatRooms: OBPEndpoint = {
16412+
case "chat-rooms" :: "search" :: Nil JsonPost json -> _ => {
16413+
cc => implicit val ec = EndpointContext(Some(cc))
16414+
for {
16415+
(Full(u), callContext) <- authenticatedAccess(cc)
16416+
postJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $ChatRoomSearchRequestJsonV600", 400, callContext) {
16417+
json.extract[ChatRoomSearchRequestJsonV600]
16418+
}
16419+
rooms <- Future {
16420+
code.chat.ChatRoomTrait.chatRoomProvider.vend.searchChatRoomsForUserWithParticipants(
16421+
u.userId,
16422+
postJson.with_user_ids,
16423+
postJson.exact_participants.getOrElse(false)
16424+
)
16425+
} map {
16426+
x => unboxFullOrFail(x, callContext, s"$UnknownError Cannot search chat rooms", 400)
16427+
}
16428+
unreadCounts <- Future {
16429+
computeUnreadCounts(rooms, u.userId)
16430+
}
16431+
} yield {
16432+
(JSONFactory600.createChatRoomsJson(rooms, unreadCounts), HttpCode.`200`(callContext))
16433+
}
16434+
}
16435+
}
16436+
1635416437
// 26. getMyUnreadCounts
1635516438
staticResourceDocs += ResourceDoc(
1635616439
getMyUnreadCounts,

obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,10 @@ case class InvestigationReportJsonV600(
11751175
// Chat / Messaging API case classes
11761176
case class PostChatRoomJsonV600(name: String, description: String)
11771177
case class PutChatRoomJsonV600(name: Option[String], description: Option[String])
1178+
case class ChatRoomSearchRequestJsonV600(
1179+
with_user_ids: List[String],
1180+
exact_participants: Option[Boolean] = Some(false)
1181+
)
11781182
case class PostParticipantJsonV600(user_id: Option[String], consumer_id: Option[String], permissions: Option[List[String]], webhook_url: Option[String])
11791183
case class PutParticipantPermissionsJsonV600(permissions: List[String])
11801184
case class PostChatMessageJsonV600(content: String, message_type: Option[String], mentioned_user_ids: Option[List[String]], reply_to_message_id: Option[String], thread_id: Option[String])

obp-api/src/main/scala/code/chat/ChatRoomTrait.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ trait ChatRoomProvider {
2323
def getChatRoomsByBankIdForUser(bankId: String, userId: String): Box[List[ChatRoomTrait]]
2424
def getChatRoomByJoiningKey(joiningKey: String): Box[ChatRoomTrait]
2525

26+
/**
27+
* Find chat rooms where the given user AND all of `requiredParticipantUserIds`
28+
* are participants. If `exactParticipants` is true, the room's participant set
29+
* must equal exactly `{userId} ∪ requiredParticipantUserIds` and open rooms
30+
* are excluded (their participant set is "everyone" so an exact match is
31+
* meaningless).
32+
*/
33+
def searchChatRoomsForUserWithParticipants(
34+
userId: String,
35+
requiredParticipantUserIds: List[String],
36+
exactParticipants: Boolean
37+
): Box[List[ChatRoomTrait]]
38+
2639
def updateChatRoom(
2740
chatRoomId: String,
2841
name: Option[String],

obp-api/src/main/scala/code/chat/MappedChatRoom.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,46 @@ object MappedChatRoomProvider extends ChatRoomProvider {
6363
ChatRoom.find(By(ChatRoom.JoiningKey, joiningKey))
6464
}
6565

66+
override def searchChatRoomsForUserWithParticipants(
67+
userId: String,
68+
requiredParticipantUserIds: List[String],
69+
exactParticipants: Boolean
70+
): Box[List[ChatRoomTrait]] = {
71+
tryo {
72+
// 1. Find every room where the current user is an explicit participant.
73+
val myRoomIds = Participant.findAll(By(Participant.UserId, userId))
74+
.map(_.chatRoomId)
75+
.distinct
76+
val myRooms = if (myRoomIds.isEmpty) Nil
77+
else ChatRoom.findAll(ByList(ChatRoom.ChatRoomId, myRoomIds))
78+
79+
// 2. For each candidate room, fetch the full participant set and apply
80+
// the requested filters.
81+
val requiredSet = requiredParticipantUserIds.toSet
82+
val expectedExactSize = requiredSet.size + 1 // +1 for the current user
83+
84+
myRooms.filter { room =>
85+
// Open rooms have implicit participants ("everyone"), so an exact-match
86+
// query is meaningless against them — exclude them in that case.
87+
if (exactParticipants && room.isOpenRoom) {
88+
false
89+
} else {
90+
val participantUserIds = Participant.findAll(By(Participant.ChatRoomId, room.chatRoomId))
91+
.map(_.userId)
92+
.toSet
93+
val containsAllRequired = requiredSet.subsetOf(participantUserIds)
94+
if (!containsAllRequired) {
95+
false
96+
} else if (exactParticipants) {
97+
participantUserIds.size == expectedExactSize
98+
} else {
99+
true
100+
}
101+
}
102+
}
103+
}
104+
}
105+
66106
override def updateChatRoom(
67107
chatRoomId: String,
68108
name: Option[String],

0 commit comments

Comments
 (0)