summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <coding@schilljs.com>2022-12-08 11:54:45 +0100
committerJoas Schilling <coding@schilljs.com>2022-12-12 12:08:24 +0100
commita0ab39389fc849610c9e3faf89ea414f701ab0bf (patch)
tree652c2beb223128b91fbeb346b47c377eca26c763
parentd390d3bec3b0efddf580a4e45c527430b1c4c830 (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.php2
-rw-r--r--docs/breakout-rooms.md6
-rw-r--r--docs/conversation.md18
-rw-r--r--lib/Controller/RoomController.php48
-rw-r--r--lib/Service/BreakoutRoomService.php43
-rw-r--r--tests/integration/features/bootstrap/FeatureContext.php33
-rw-r--r--tests/integration/features/conversation/breakout-rooms.feature209
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 |
+ |