diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2023-02-02 07:00:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-02 07:00:50 +0100 |
commit | 820839c5f0fb28613c4b9fb5c08e41a842f703ac (patch) | |
tree | 1984d7e4a0ee2dd533f3f39e2adcbd4b882d97a1 | |
parent | a37667a99ea2c9c69b630f3ca76fac84278d79e7 (diff) | |
parent | 54bc20ce6b0e36f328c5e11c6e58581acdd0b783 (diff) |
Merge pull request #8660 from nextcloud/feature/8610/add-endpoint-to-return-all-participants
Add an endpoint to return all participants
-rw-r--r-- | appinfo/routes/routesRoomController.php | 2 | ||||
-rw-r--r-- | docs/breakout-rooms.md | 18 | ||||
-rw-r--r-- | docs/participant.md | 10 | ||||
-rw-r--r-- | lib/Controller/RoomController.php | 33 | ||||
-rw-r--r-- | lib/Participant.php | 4 | ||||
-rw-r--r-- | lib/Service/ParticipantService.php | 44 | ||||
-rw-r--r-- | tests/integration/features/bootstrap/FeatureContext.php | 36 | ||||
-rw-r--r-- | tests/integration/features/conversation/breakout-rooms.feature | 12 |
8 files changed, 149 insertions, 10 deletions
diff --git a/appinfo/routes/routesRoomController.php b/appinfo/routes/routesRoomController.php index 9bbe59a94..7542b8884 100644 --- a/appinfo/routes/routesRoomController.php +++ b/appinfo/routes/routesRoomController.php @@ -66,6 +66,8 @@ return [ ])], /** @see \OCA\Talk\Controller\RoomController::getParticipants() */ ['name' => 'Room#getParticipants', 'url' => '/api/{apiVersion}/room/{token}/participants', 'verb' => 'GET', 'requirements' => $requirementsWithToken], + /** @see \OCA\Talk\Controller\RoomController::getBreakoutRoomParticipants() */ + ['name' => 'Room#getBreakoutRoomParticipants', 'url' => '/api/{apiVersion}/room/{token}/breakout-rooms/participants', 'verb' => 'GET', 'requirements' => $requirementsWithToken], /** @see \OCA\Talk\Controller\RoomController::addParticipantToRoom() */ ['name' => 'Room#addParticipantToRoom', 'url' => '/api/{apiVersion}/room/{token}/participants', 'verb' => 'POST', 'requirements' => $requirementsWithToken], /** @see \OCA\Talk\Controller\RoomController::removeSelfFromRoom() */ diff --git a/docs/breakout-rooms.md b/docs/breakout-rooms.md index 2e160f99c..c77a9d9be 100644 --- a/docs/breakout-rooms.md +++ b/docs/breakout-rooms.md @@ -36,7 +36,7 @@ Group and public conversations can be used to host breakout rooms. + `403 Forbidden` When the current user is not a moderator/owner + `404 Not Found` When the conversation could not be found for the participant - Data: Array of conversations (breakout rooms and parent) - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## Remove breakout rooms @@ -50,7 +50,7 @@ Group and public conversations can be used to host breakout rooms. + `403 Forbidden` When the current user is not a moderator/owner + `404 Not Found` When the conversation could not be found for the participant - Data: Parent conversation - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## Start breakout rooms @@ -65,7 +65,7 @@ Group and public conversations can be used to host breakout rooms. + `403 Forbidden` When the current user is not a moderator/owner + `404 Not Found` When the conversation could not be found for the participant - Data: Array of conversations (breakout rooms and parent) - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## Stop breakout rooms @@ -80,7 +80,7 @@ Group and public conversations can be used to host breakout rooms. + `403 Forbidden` When the current user is not a moderator/owner + `404 Not Found` When the conversation could not be found for the participant - Data: Array of conversations (breakout rooms and parent) - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## Broadcast message to breakout rooms @@ -102,7 +102,7 @@ Group and public conversations can be used to host breakout rooms. + `404 Not Found` When the conversation could not be found for the participant + `413 Payload Too Large` When the message was longer than the allowed limit of 32000 characters (check the `spreed => config => chat => max-length` capability for the limit) - Data: Array of conversations (breakout rooms and parent) - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## Reorganize attendees @@ -124,7 +124,7 @@ Group and public conversations can be used to host breakout rooms. + `403 Forbidden` When the current user is not a moderator/owner + `404 Not Found` When the conversation could not be found for the participant - Data: Array of conversations (breakout rooms and parent) - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## Request assistance @@ -139,7 +139,7 @@ This endpoint allows participants to raise their hand (token is the breakout roo + `400 Bad Request` Error `room`: When the room is not a breakout room or breakout rooms are not started + `404 Not Found` When the conversation could not be found for the participant - Data: Breakout room - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## Reset request for assistance @@ -152,7 +152,7 @@ This endpoint allows participants to raise their hand (token is the breakout roo + `400 Bad Request` Error `room`: When the room is not a breakout room or breakout rooms are not started + `404 Not Found` When the conversation could not be found for the participant - Data: Breakout room - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) ## List all breakout rooms @@ -181,4 +181,4 @@ This endpoint allows participants to raise their hand (token is the breakout roo + `400 Bad Request` Error `target`: When the target room is not breakout room of the parent + `404 Not Found` When the conversation could not be found for the participant - Data: Target breakout room - See array definition in `Get user´s conversations` (API v4) + See array definition in [Get user´s conversations](conversation.md#get-user-s-conversations) (API v4) diff --git a/docs/participant.md b/docs/participant.md index 0a1ffdd08..f3faccf04 100644 --- a/docs/participant.md +++ b/docs/participant.md @@ -41,6 +41,16 @@ | `status` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation | | `statusIcon` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation | | `statusMessage` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation | +| `roomToken` | string | v4 | | Optional: Only available with `breakout-rooms-v1` capability | + + +## Get list of participants in a conversation including its breakout rooms + +* Required capability: `breakout-rooms-v1` +* Method: `GET` +* Endpoint: `/room/{token}/breakout-rooms/participants` +* Data: See Data in [Get list of participants in a conversations](participant.md#get-list-of-participants-in-a-conversation) +* Response: See Response in [Get list of participants in a conversations](participant.md#get-list-of-participants-in-a-conversation) ## Add a participant to a conversation diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 2f725cc00..0e44e69a4 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -699,9 +699,39 @@ class RoomController extends AEnvironmentAwareController { return new DataResponse([], Http::STATUS_FORBIDDEN); } - $maxPingAge = $this->timeFactory->getTime() - Session::SESSION_TIMEOUT_KILL; $participants = $this->participantService->getSessionsAndParticipantsForRoom($this->room); + + return $this->formatParticipantList($participants, $includeStatus); + } + + /** + * @PublicPage + * @RequireParticipant + * @RequireModeratorOrNoLobby + * + * @param bool $includeStatus + * @return DataResponse + */ + public function getBreakoutRoomParticipants(bool $includeStatus = false): DataResponse { + if ($this->participant->getAttendee()->getParticipantType() === Participant::GUEST) { + return new DataResponse([], Http::STATUS_FORBIDDEN); + } + + $breakoutRooms = $this->breakoutRoomService->getBreakoutRooms($this->room, $this->participant); + $breakoutRooms[] = $this->room; + $participants = $this->participantService->getSessionsAndParticipantsForRooms($breakoutRooms); + + return $this->formatParticipantList($participants, $includeStatus); + } + + /** + * @param Participant[] $participants + * @param bool $includeStatus + * @return DataResponse + */ + protected function formatParticipantList(array $participants, bool $includeStatus): DataResponse { $results = $headers = $statuses = []; + $maxPingAge = $this->timeFactory->getTime() - Session::SESSION_TIMEOUT_KILL; if ($this->userId !== null && $includeStatus @@ -747,6 +777,7 @@ class RoomController extends AEnvironmentAwareController { } $result = [ + 'roomToken' => $participant->getRoom()->getToken(), 'inCall' => Participant::FLAG_DISCONNECTED, 'lastPing' => 0, 'sessionIds' => [], diff --git a/lib/Participant.php b/lib/Participant.php index 83e80f7f5..b11a9f74b 100644 --- a/lib/Participant.php +++ b/lib/Participant.php @@ -67,6 +67,10 @@ class Participant { $this->session = $session; } + public function getRoom(): Room { + return $this->room; + } + public function getAttendee(): Attendee { return $this->attendee; } diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php index 2941972c8..f03fad5e6 100644 --- a/lib/Service/ParticipantService.php +++ b/lib/Service/ParticipantService.php @@ -1267,6 +1267,34 @@ class ParticipantService { } /** + * Get all sessions and attendees without a session for the room + * + * This will return multiple items for the same attendee if the attendee + * has multiple sessions in the room. + * + * @param Room[] $rooms + * @return Participant[] + */ + public function getSessionsAndParticipantsForRooms(array $rooms): array { + $roomIds = array_map(static fn (Room $room) => $room->getId(), $rooms); + $map = array_combine($roomIds, $rooms); + + $query = $this->connection->getQueryBuilder(); + + $helper = new SelectHelper(); + $helper->selectAttendeesTable($query); + $helper->selectSessionsTable($query); + $query->from('talk_attendees', 'a') + ->leftJoin( + 'a', 'talk_sessions', 's', + $query->expr()->eq('s.attendee_id', 'a.id') + ) + ->where($query->expr()->in('a.room_id', $query->createNamedParameter($roomIds, IQueryBuilder::PARAM_INT_ARRAY))); + + return $this->getParticipantsForRoomsFromQuery($query, $map); + } + + /** * @param Room $room * @param int $maxAge * @return Participant[] @@ -1350,12 +1378,28 @@ class ParticipantService { /** * @param IQueryBuilder $query + * @param Room $room * @return Participant[] */ protected function getParticipantsFromQuery(IQueryBuilder $query, Room $room): array { + return $this->getParticipantsForRoomsFromQuery($query, [$room->getId() => $room]); + } + + /** + * @param IQueryBuilder $query + * @param Room[] $rooms Room ID => Room object + * @psalm-param array<int, Room> $rooms + * @return Participant[] + */ + protected function getParticipantsForRoomsFromQuery(IQueryBuilder $query, array $rooms): array { $participants = []; $result = $query->executeQuery(); while ($row = $result->fetch()) { + $room = $rooms[(int) $row['room_id']] ?? null; + if ($room === null) { + continue; + } + $attendee = $this->attendeeMapper->createAttendeeFromRow($row); if (isset($row['s_id'])) { $session = $this->sessionMapper->createSessionFromRow($row); diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index aa5729251..f763bdfda 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -580,11 +580,44 @@ class FeatureContext implements Context, SnippetAcceptingContext { if ($formData instanceof TableNode) { $attendees = $this->getDataFromResponse($this->response); + } else { + $attendees = []; + } + $this->assertAttendeeList($identifier, $formData, $attendees); + } + + /** + * @Then /^user "([^"]*)" sees the following attendees in breakout rooms for room "([^"]*)" with (\d+) \((v4)\)$/ + * + * @param string $user + * @param string $identifier + * @param int $statusCode + * @param string $apiVersion + * @param TableNode $formData + */ + public function userSeesAttendeesInBreakoutRoomsForRoom(string $user, string $identifier, int $statusCode, string $apiVersion, TableNode $formData = null): void { + $this->setCurrentUser($user); + $this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/breakout-rooms/participants'); + $this->assertStatusCode($this->response, $statusCode); + + if ($formData instanceof TableNode) { + $attendees = $this->getDataFromResponse($this->response); + } else { + $attendees = []; + } + $this->assertAttendeeList($identifier, $formData, $attendees); + } + + protected function assertAttendeeList(string $identifier, ?TableNode $formData, array $attendees): void { + if ($formData instanceof TableNode) { $expectedKeys = array_flip($formData->getRows()[0]); $result = []; foreach ($attendees as $attendee) { $data = []; + if (isset($expectedKeys['roomToken'])) { + $data['roomToken'] = self::$tokenToIdentifier[$attendee['roomToken']]; + } if (isset($expectedKeys['actorType'])) { $data['actorType'] = $attendee['actorType']; } @@ -676,6 +709,9 @@ class FeatureContext implements Context, SnippetAcceptingContext { } protected static function sortAttendees(array $a1, array $a2): int { + if (array_key_exists('roomToken', $a1) && array_key_exists('roomToken', $a2) && $a1['roomToken'] !== $a2['roomToken']) { + return $a1['roomToken'] <=> $a2['roomToken']; + } if (array_key_exists('participantType', $a1) && array_key_exists('participantType', $a2) && $a1['participantType'] !== $a2['participantType']) { return $a1['participantType'] <=> $a2['participantType']; } diff --git a/tests/integration/features/conversation/breakout-rooms.feature b/tests/integration/features/conversation/breakout-rooms.feature index 279fe5e09..aedc91e6f 100644 --- a/tests/integration/features/conversation/breakout-rooms.feature +++ b/tests/integration/features/conversation/breakout-rooms.feature @@ -41,6 +41,18 @@ Feature: conversation/breakout-rooms | type | name | | 2 | class room | | 2 | Room 3 | + And user "participant1" sees the following attendees in breakout rooms for room "class room" with 200 (v4) + | roomToken | actorType | actorId | participantType | + | class room | users | participant1 | 1 | + | class room | users | participant2 | 3 | + | class room | users | participant3 | 3 | + | class room | users | participant4 | 3 | + | Room 1 | users | participant1 | 1 | + | Room 1 | users | participant2 | 3 | + | Room 2 | users | participant1 | 1 | + | Room 2 | users | participant3 | 3 | + | Room 3 | users | participant1 | 1 | + | Room 3 | users | participant4 | 3 | Scenario: Teacher creates automatic breakout rooms Given user "participant1" creates room "class room" (v4) |