diff options
author | Joas Schilling <coding@schilljs.com> | 2022-12-08 11:54:45 +0100 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2022-12-12 12:08:24 +0100 |
commit | a0ab39389fc849610c9e3faf89ea414f701ab0bf (patch) | |
tree | 652c2beb223128b91fbeb346b47c377eca26c763 | |
parent | d390d3bec3b0efddf580a4e45c527430b1c4c830 (diff) |
Add an API that allows students and teachers so list the breakout rooms
Signed-off-by: Joas Schilling <coding@schilljs.com>
-rw-r--r-- | appinfo/routes/routesRoomController.php | 2 | ||||
-rw-r--r-- | docs/breakout-rooms.md | 6 | ||||
-rw-r--r-- | docs/conversation.md | 18 | ||||
-rw-r--r-- | lib/Controller/RoomController.php | 48 | ||||
-rw-r--r-- | lib/Service/BreakoutRoomService.php | 43 | ||||
-rw-r--r-- | tests/integration/features/bootstrap/FeatureContext.php | 33 | ||||
-rw-r--r-- | tests/integration/features/conversation/breakout-rooms.feature | 209 |
7 files changed, 275 insertions, 84 deletions
diff --git a/appinfo/routes/routesRoomController.php b/appinfo/routes/routesRoomController.php index 3cc483c01..9bbe59a94 100644 --- a/appinfo/routes/routesRoomController.php +++ b/appinfo/routes/routesRoomController.php @@ -42,6 +42,8 @@ return [ ['name' => 'Room#createRoom', 'url' => '/api/{apiVersion}/room', 'verb' => 'POST', 'requirements' => $requirements], /** @see \OCA\Talk\Controller\RoomController::getSingleRoom() */ ['name' => 'Room#getSingleRoom', 'url' => '/api/{apiVersion}/room/{token}', 'verb' => 'GET', 'requirements' => $requirementsWithToken], + /** @see \OCA\Talk\Controller\RoomController::getBreakoutRooms() */ + ['name' => 'Room#getBreakoutRooms', 'url' => '/api/{apiVersion}/room/{token}/breakout-rooms', 'verb' => 'GET', 'requirements' => $requirementsWithToken], /** @see \OCA\Talk\Controller\RoomController::renameRoom() */ ['name' => 'Room#renameRoom', 'url' => '/api/{apiVersion}/room/{token}', 'verb' => 'PUT', 'requirements' => $requirementsWithToken], /** @see \OCA\Talk\Controller\RoomController::deleteRoom() */ diff --git a/docs/breakout-rooms.md b/docs/breakout-rooms.md index 58cf124fb..7f2c8ab25 100644 --- a/docs/breakout-rooms.md +++ b/docs/breakout-rooms.md @@ -28,7 +28,7 @@ Group and public conversations can be used to host breakout rooms. + `200 OK` + `400 Bad Request` When breakout rooms are disabled on the server + `400 Bad Request` When breakout rooms are already configured - + `400 Bad Request` When the conversation is not a group or public conversation + + `400 Bad Request` When the conversation is not a group conversation + `400 Bad Request` When the conversation is a breakout room itself + `400 Bad Request` When the mode is invalid + `400 Bad Request` When the amount is below the minimum or above the maximum @@ -117,3 +117,7 @@ This endpoint allows participants to raise their hand (token is the breakout roo + `200 OK` + `400 Bad Request` When the room does not have breakout rooms configured + `404 Not Found` When the conversation could not be found for the participant + +## List all breakout rooms + +See [conversation API](conversation.md#get-breakout-rooms)) diff --git a/docs/conversation.md b/docs/conversation.md index 16dccaa26..7948deeb0 100644 --- a/docs/conversation.md +++ b/docs/conversation.md @@ -153,6 +153,24 @@ - Data: See array definition in `Get user´s conversations` +## Get breakout rooms + +Get all (for moderators and in case of "free selection) or the assigned breakout room + +* Required capability: `breakout-rooms-v1` +* Method: `GET` +* Endpoint: `/room/{token}/breakout-rooms` + +* Response: + - Status code: + + `200 OK` + + `400 Bad Request` When the conversation does not have breakout rooms configured + + `400 Bad Request` When the breakout rooms are not started and the participant is not a moderator + + `401 Unauthorized` When the user is not logged in + + `404 Not Found` When the conversation could not be found for the participant + + - Data: See array definition in `Get user´s conversations` + ## Rename a conversation * Method: `PUT` diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index fa859c268..c0a2b6f7f 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -46,6 +46,7 @@ use OCA\Talk\Model\Session; use OCA\Talk\Participant; use OCA\Talk\Room; use OCA\Talk\Service\AvatarService; +use OCA\Talk\Service\BreakoutRoomService; use OCA\Talk\Service\ParticipantService; use OCA\Talk\Service\RoomService; use OCA\Talk\Service\SessionService; @@ -83,6 +84,7 @@ class RoomController extends AEnvironmentAwareController { protected Manager $manager; protected ICloudIdManager $cloudIdManager; protected RoomService $roomService; + protected BreakoutRoomService $breakoutRoomService; protected ParticipantService $participantService; protected SessionService $sessionService; protected GuestManager $guestManager; @@ -109,6 +111,7 @@ class RoomController extends AEnvironmentAwareController { IGroupManager $groupManager, Manager $manager, RoomService $roomService, + BreakoutRoomService $breakoutRoomService, ParticipantService $participantService, SessionService $sessionService, GuestManager $guestManager, @@ -132,6 +135,7 @@ class RoomController extends AEnvironmentAwareController { $this->groupManager = $groupManager; $this->manager = $manager; $this->roomService = $roomService; + $this->breakoutRoomService = $breakoutRoomService; $this->participantService = $participantService; $this->sessionService = $sessionService; $this->guestManager = $guestManager; @@ -266,6 +270,39 @@ class RoomController extends AEnvironmentAwareController { return new DataResponse($return, Http::STATUS_OK); } + /** + * Get all (for moderators and in case of "free selection) or the assigned breakout room + * + * @NoAdminRequired + * @RequireLoggedInParticipant + * @BruteForceProtection(action=talkRoomToken) + * + * @return DataResponse + */ + public function getBreakoutRooms(): DataResponse { + try { + $rooms = $this->breakoutRoomService->getBreakoutRooms($this->room, $this->participant); + } catch (InvalidArgumentException $e) { + return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST); + } + + $return = []; + foreach ($rooms as $room) { + try { + $participant = $this->participantService->getParticipant($room, $this->userId); + } catch (ParticipantNotFoundException $e) { + $participant = null; + } + + try { + $return[] = $this->formatRoom($room, $participant, null, false, true); + } catch (\RuntimeException $e) { + } + } + + + return new DataResponse($return); + } /** * @PublicPage @@ -357,11 +394,12 @@ class RoomController extends AEnvironmentAwareController { * @param Participant|null $currentParticipant * @param array|null $statuses * @param bool $isSIPBridgeRequest + * @param bool $isListingBreakoutRooms * @return array * @throws RoomNotFoundException */ - protected function formatRoom(Room $room, ?Participant $currentParticipant, ?array $statuses = null, bool $isSIPBridgeRequest = false): array { - return $this->formatRoomV4($room, $currentParticipant, $statuses, $isSIPBridgeRequest); + protected function formatRoom(Room $room, ?Participant $currentParticipant, ?array $statuses = null, bool $isSIPBridgeRequest = false, bool $isListingBreakoutRooms = false): array { + return $this->formatRoomV4($room, $currentParticipant, $statuses, $isSIPBridgeRequest, $isListingBreakoutRooms); } /** @@ -369,10 +407,11 @@ class RoomController extends AEnvironmentAwareController { * @param Participant|null $currentParticipant * @param array|null $statuses * @param bool $isSIPBridgeRequest + * @param bool $isListingBreakoutRooms * @return array * @throws RoomNotFoundException */ - protected function formatRoomV4(Room $room, ?Participant $currentParticipant, ?array $statuses, bool $isSIPBridgeRequest): array { + protected function formatRoomV4(Room $room, ?Participant $currentParticipant, ?array $statuses, bool $isSIPBridgeRequest, bool $isListingBreakoutRooms): array { $roomData = [ 'id' => $room->getId(), 'token' => $room->getToken(), @@ -438,6 +477,7 @@ class RoomController extends AEnvironmentAwareController { } if ($isSIPBridgeRequest + || ($isListingBreakoutRooms && !$currentParticipant instanceof Participant) || ($room->getListable() !== Room::LISTABLE_NONE && !$currentParticipant instanceof Participant) ) { return array_merge($roomData, [ @@ -453,6 +493,8 @@ class RoomController extends AEnvironmentAwareController { 'lobbyTimer' => $lobbyTimer, 'sipEnabled' => $room->getSIPEnabled(), 'listable' => $room->getListable(), + 'breakoutRoomMode' => $room->getBreakoutRoomMode(), + 'breakoutRoomStatus' => $room->getBreakoutRoomStatus(), ]); } diff --git a/lib/Service/BreakoutRoomService.php b/lib/Service/BreakoutRoomService.php index a9f02b820..430c67de8 100644 --- a/lib/Service/BreakoutRoomService.php +++ b/lib/Service/BreakoutRoomService.php @@ -28,6 +28,7 @@ namespace OCA\Talk\Service; use InvalidArgumentException; use OCA\Talk\Chat\ChatManager; use OCA\Talk\Config; +use OCA\Talk\Exceptions\ParticipantNotFoundException; use OCA\Talk\Manager; use OCA\Talk\Model\Attendee; use OCA\Talk\Model\BreakoutRoom; @@ -84,9 +85,8 @@ class BreakoutRoomService { throw new InvalidArgumentException('room'); } - if ($parent->getType() !== Room::TYPE_GROUP - && $parent->getType() !== Room::TYPE_PUBLIC) { - // Can only do breakout rooms in group and public rooms + if ($parent->getType() !== Room::TYPE_GROUP) { + // Can only do breakout rooms in group rooms throw new InvalidArgumentException('room'); } @@ -325,4 +325,41 @@ class BreakoutRoomService { // FIXME missing to send the signaling messages so participants are moved back } + + /** + * @param Room $parent + * @param Participant $participant + * @return Room[] + */ + public function getBreakoutRooms(Room $parent, Participant $participant): array { + if ($parent->getBreakoutRoomMode() === BreakoutRoom::MODE_NOT_CONFIGURED) { + throw new \InvalidArgumentException('mode'); + } + + if (!$participant->hasModeratorPermissions() && $parent->getBreakoutRoomStatus() !== BreakoutRoom::STATUS_STARTED) { + throw new \InvalidArgumentException('status'); + } + + $breakoutRooms = $this->manager->getMultipleRoomsByObject(BreakoutRoom::PARENT_OBJECT_TYPE, $parent->getToken()); + + $returnAll = $participant->hasModeratorPermissions() || $parent->getBreakoutRoomMode() === BreakoutRoom::MODE_FREE; + if (!$returnAll) { + $rooms = []; + foreach ($breakoutRooms as $breakoutRoom) { + try { + $this->participantService->getParticipantByActor( + $breakoutRoom, + $participant->getAttendee()->getActorType(), + $participant->getAttendee()->getActorId() + ); + $rooms[] = $breakoutRoom; + } catch (ParticipantNotFoundException $e){ + // Skip this room + } + } + } else { + $rooms = $breakoutRooms; + } + return $rooms; + } } diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index e41000d89..dbf7ac80f 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -283,6 +283,39 @@ class FeatureContext implements Context, SnippetAcceptingContext { } /** + * @Then /^user "([^"]*)" sees the following breakout rooms for room "([^"]*)" with (\d+) \((v4)\)$/ + * + * @param string $user + * @param string $apiVersion + * @param int $status + * @param TableNode|null $formData + */ + public function userListsBreakoutRooms(string $user, string $identifier, int $status, string $apiVersion, TableNode $formData = null): void { + $token = self::$identifierToToken[$identifier]; + + $this->setCurrentUser($user); + $this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/room/' . $token . '/breakout-rooms'); + $this->assertStatusCode($this->response, $status); + + if ($status !== 200) { + return; + } + + $rooms = $this->getDataFromResponse($this->response); + + $rooms = array_filter($rooms, function ($room) { + return $room['type'] !== 4; + }); + + if ($formData === null) { + Assert::assertEmpty($rooms); + return; + } + + $this->assertRooms($rooms, $formData); + } + + /** * @param array $rooms * @param TableNode $formData */ diff --git a/tests/integration/features/conversation/breakout-rooms.feature b/tests/integration/features/conversation/breakout-rooms.feature index 433655d81..352adcb56 100644 --- a/tests/integration/features/conversation/breakout-rooms.feature +++ b/tests/integration/features/conversation/breakout-rooms.feature @@ -7,7 +7,7 @@ Feature: conversation/breakout-rooms Scenario: Teacher creates manual breakout rooms Given user "participant1" creates room "class room" (v4) - | roomType | 3 | + | roomType | 2 | | roomName | class room | And user "participant1" adds user "participant2" to room "class room" with 200 (v4) And user "participant1" adds user "participant3" to room "class room" with 200 (v4) @@ -24,26 +24,26 @@ Feature: conversation/breakout-rooms | users::participant4 | 2 | Then user "participant1" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 1 | - | 3 | Room 2 | - | 3 | Room 3 | + | 2 | class room | + | 2 | Room 1 | + | 2 | Room 2 | + | 2 | Room 3 | Then user "participant2" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 1 | + | 2 | class room | + | 2 | Room 1 | Then user "participant3" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 2 | + | 2 | class room | + | 2 | Room 2 | Then user "participant4" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 3 | + | 2 | class room | + | 2 | Room 3 | Scenario: Teacher creates automatic breakout rooms Given user "participant1" creates room "class room" (v4) - | roomType | 3 | + | roomType | 2 | | roomName | class room | And user "participant1" adds user "participant2" to room "class room" with 200 (v4) And user "participant1" adds user "participant3" to room "class room" with 200 (v4) @@ -57,10 +57,10 @@ Feature: conversation/breakout-rooms When user "participant1" creates 3 automatic breakout rooms for "class room" with 200 (v1) Then user "participant1" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 1 | - | 3 | Room 2 | - | 3 | Room 3 | + | 2 | class room | + | 2 | Room 1 | + | 2 | Room 2 | + | 2 | Room 3 | And user "participant1" sees the following attendees in room "Room 1" with 200 (v4) | actorType | actorId | participantType | | users | participant1 | 1 | @@ -75,20 +75,20 @@ Feature: conversation/breakout-rooms | users | /^participant\d$/ | 3 | Then user "participant2" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | /^Room \d$/ | + | 2 | class room | + | 2 | /^Room \d$/ | Then user "participant3" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | /^Room \d$/ | + | 2 | class room | + | 2 | /^Room \d$/ | Then user "participant4" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | /^Room \d$/ | + | 2 | class room | + | 2 | /^Room \d$/ | Scenario: Co-teachers are promoted and removed in all breakout rooms Given user "participant1" creates room "class room" (v4) - | roomType | 3 | + | roomType | 2 | | roomName | class room | And user "participant1" adds user "participant2" to room "class room" with 200 (v4) And user "participant1" sees the following attendees in room "class room" with 200 (v4) @@ -99,21 +99,21 @@ Feature: conversation/breakout-rooms | users::participant2 | 0 | And user "participant1" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 1 | - | 3 | Room 2 | - | 3 | Room 3 | + | 2 | class room | + | 2 | Room 1 | + | 2 | Room 2 | + | 2 | Room 3 | And user "participant2" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 1 | + | 2 | class room | + | 2 | Room 1 | When user "participant1" promotes "participant2" in room "class room" with 200 (v4) Then user "participant2" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 1 | - | 3 | Room 2 | - | 3 | Room 3 | + | 2 | class room | + | 2 | Room 1 | + | 2 | Room 2 | + | 2 | Room 3 | And user "participant1" sees the following attendees in room "Room 1" with 200 (v4) | actorType | actorId | participantType | | users | participant1 | 1 | @@ -129,7 +129,7 @@ Feature: conversation/breakout-rooms When user "participant1" demotes "participant2" in room "class room" with 200 (v4) Then user "participant2" is participant of the following rooms (v4) | type | name | - | 3 | class room | + | 2 | class room | And user "participant1" sees the following attendees in room "Room 1" with 200 (v4) | actorType | actorId | participantType | | users | participant1 | 1 | @@ -142,15 +142,15 @@ Feature: conversation/breakout-rooms Scenario: Can not nest breakout rooms When user "participant1" creates room "class room" (v4) - | roomType | 3 | + | roomType | 2 | | roomName | class room | And user "participant1" creates 3 manual breakout rooms for "class room" with 200 (v1) And user "participant1" is participant of the following rooms (v4) | type | name | - | 3 | class room | - | 3 | Room 1 | - | 3 | Room 2 | - | 3 | Room 3 | + | 2 | class room | + | 2 | Room 1 | + | 2 | Room 2 | + | 2 | Room 3 | And user "participant1" creates 3 manual breakout rooms for "Room 1" with 400 (v1) Scenario: Can not create breakout rooms in one-to-one @@ -161,19 +161,19 @@ Feature: conversation/breakout-rooms Scenario: Can not create more than 20 breakout rooms When user "participant1" creates room "class room" (v4) - | roomType | 3 | + | roomType | 2 | | roomName | class room | And user "participant1" creates 21 manual breakout rooms for "class room" with 400 (v1) Scenario: Can not create less than 1 breakout rooms When user "participant1" creates room "class room" (v4) - | roomType | 3 | + | |