Browse Source

Merge pull request #8442 from nextcloud/feature/8338/breakout-rooms-api-3

🖖 Breakout rooms 🛠️ API - Part 3
pull/8467/head
Vitor Mattos 3 years ago
committed by GitHub
parent
commit
ad7e93c02f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      appinfo/routes/routesBreakoutRoomController.php
  2. 2
      appinfo/routes/routesRoomController.php
  3. 52
      docs/breakout-rooms.md
  4. 18
      docs/conversation.md
  5. 46
      lib/Controller/BreakoutRoomController.php
  6. 69
      lib/Controller/RoomController.php
  7. 2
      lib/Model/BreakoutRoom.php
  8. 138
      lib/Service/BreakoutRoomService.php
  9. 2
      lib/Service/RoomService.php
  10. 74
      tests/integration/features/bootstrap/FeatureContext.php
  11. 309
      tests/integration/features/conversation/breakout-rooms.feature

6
appinfo/routes/routesBreakoutRoomController.php

@ -36,9 +36,15 @@ return [
['name' => 'BreakoutRoom#removeBreakoutRooms', 'url' => '/api/{apiVersion}/breakout-rooms/{token}', 'verb' => 'DELETE', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BreakoutRoomController::broadcastChatMessage() */
['name' => 'BreakoutRoom#broadcastChatMessage', 'url' => '/api/{apiVersion}/breakout-rooms/{token}/broadcast', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BreakoutRoomController::requestAssistance() */
['name' => 'BreakoutRoom#requestAssistance', 'url' => '/api/{apiVersion}/breakout-rooms/{token}/request-assistance', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BreakoutRoomController::resetRequestForAssistance() */
['name' => 'BreakoutRoom#resetRequestForAssistance', 'url' => '/api/{apiVersion}/breakout-rooms/{token}/request-assistance', 'verb' => 'DELETE', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BreakoutRoomController::startBreakoutRooms() */
['name' => 'BreakoutRoom#startBreakoutRooms', 'url' => '/api/{apiVersion}/breakout-rooms/{token}/rooms', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BreakoutRoomController::stopBreakoutRooms() */
['name' => 'BreakoutRoom#stopBreakoutRooms', 'url' => '/api/{apiVersion}/breakout-rooms/{token}/rooms', 'verb' => 'DELETE', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BreakoutRoomController::switchBreakoutRoom() */
['name' => 'BreakoutRoom#switchBreakoutRoom', 'url' => '/api/{apiVersion}/breakout-rooms/{token}/switch', 'verb' => 'POST', 'requirements' => $requirements],
],
];

2
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() */

52
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
@ -93,3 +93,53 @@ Group and public conversations can be used to host breakout rooms.
+ `403 Forbidden` When the participant is not a moderator
+ `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)
## Request assistance
This endpoint allows participants to raise their hand (token is the breakout room) and moderators will see it in any of the breakout rooms as well as the parent room.
* Required capability: `breakout-rooms-v1`
* Method: `POST`
* Endpoint: `/breakout-rooms/{token}/request-assistance`
* Response:
- Status code:
+ `200 OK`
+ `400 Bad Request` 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
## Reset request for assistance
* Required capability: `breakout-rooms-v1`
* Method: `DELETE`
* Endpoint: `/breakout-rooms/{token}/request-assistance`
* Response:
- Status code:
+ `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))
## Switch to a different breakout room (as non moderator)
This endpoint allows participants to raise their hand (token is the breakout room) and moderators will see it in any of the breakout rooms as well as the parent room.
* Required capability: `breakout-rooms-v1`
* Method: `POST`
* Endpoint: `/breakout-rooms/{token}/switch`
* Data:
| field | type | Description |
|----------|--------|-------------------------------------------------------------------------------|
| `token` | string | (In the URL) Conversation token of the parent room hosting the breakout rooms |
| `target` | string | Conversation token of the target breakout room |
* Response:
- Status code:
+ `200 OK`
+ `400 Bad Request` When the participant is a moderator in the conversation
+ `400 Bad Request` When breakout rooms are not configured in `free` mode
+ `400 Bad Request` When breakout rooms are not started
+ `404 Not Found` When the conversation could not be found for the participant

18
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`

46
lib/Controller/BreakoutRoomController.php

@ -86,6 +86,38 @@ class BreakoutRoomController extends AEnvironmentAwareController {
return new DataResponse([], Http::STATUS_CREATED);
}
/**
* @NoAdminRequired
* @RequireLoggedInParticipant
*
* @return DataResponse
*/
public function requestAssistance(): DataResponse {
try {
$this->breakoutRoomService->requestAssistance($this->room);
} catch (InvalidArgumentException $e) {
return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
}
return new DataResponse();
}
/**
* @NoAdminRequired
* @RequireLoggedInParticipant
*
* @return DataResponse
*/
public function resetRequestForAssistance(): DataResponse {
try {
$this->breakoutRoomService->resetRequestForAssistance($this->room);
} catch (InvalidArgumentException $e) {
return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
}
return new DataResponse();
}
/**
* @NoAdminRequired
* @RequireLoggedInModeratorParticipant
@ -113,4 +145,18 @@ class BreakoutRoomController extends AEnvironmentAwareController {
return new DataResponse();
}
/**
* @NoAdminRequired
* @RequireLoggedInParticipant
*/
public function switchBreakoutRoom(string $target): DataResponse {
try {
$this->breakoutRoomService->switchBreakoutRoom($this->room, $this->participant, $target);
} catch (InvalidArgumentException $e) {
return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
}
return new DataResponse();
}
}

69
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;
@ -232,11 +236,9 @@ class RoomController extends AEnvironmentAwareController {
foreach ($rooms as $room) {
try {
$return[] = $this->formatRoom($room, $this->participantService->getParticipant($room, $this->userId), $statuses);
} catch (RoomNotFoundException $e) {
} catch (ParticipantNotFoundException $e) {
// for example in case the room was deleted concurrently,
// the user is not a participant any more
} catch (\RuntimeException $e) {
// the user is not a participant anymore
}
}
@ -256,16 +258,42 @@ class RoomController extends AEnvironmentAwareController {
$return = [];
foreach ($rooms as $room) {
try {
$return[] = $this->formatRoom($room, null);
} catch (RoomNotFoundException $e) {
} catch (\RuntimeException $e) {
}
$return[] = $this->formatRoom($room, null);
}
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;
}
$return[] = $this->formatRoom($room, $participant, null, false, true);
}
return new DataResponse($return);
}
/**
* @PublicPage
@ -352,27 +380,11 @@ class RoomController extends AEnvironmentAwareController {
throw new UnauthorizedException('Invalid HMAC provided');
}
/**
* @param Room $room
* @param Participant|null $currentParticipant
* @param array|null $statuses
* @param bool $isSIPBridgeRequest
* @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);
}
/**
* @param Room $room
* @param Participant|null $currentParticipant
* @param array|null $statuses
* @param bool $isSIPBridgeRequest
* @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 +450,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 +466,8 @@ class RoomController extends AEnvironmentAwareController {
'lobbyTimer' => $lobbyTimer,
'sipEnabled' => $room->getSIPEnabled(),
'listable' => $room->getListable(),
'breakoutRoomMode' => $room->getBreakoutRoomMode(),
'breakoutRoomStatus' => $room->getBreakoutRoomStatus(),
]);
}

2
lib/Model/BreakoutRoom.php

@ -33,6 +33,8 @@ class BreakoutRoom {
public const STATUS_STOPPED = 0;
public const STATUS_STARTED = 1;
public const STATUS_ASSISTANCE_RESET = 0;
public const STATUS_ASSISTANCE_REQUESTED = 2;
public const MINIMUM_ROOM_AMOUNT = 1;
public const MAXIMUM_ROOM_AMOUNT = 20;

138
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');
}
@ -277,6 +277,33 @@ class BreakoutRoomService {
}
}
public function requestAssistance(Room $breakoutRoom): void {
$this->setAssistanceRequest($breakoutRoom, BreakoutRoom::STATUS_ASSISTANCE_REQUESTED);
}
public function resetRequestForAssistance(Room $breakoutRoom): void {
$this->setAssistanceRequest($breakoutRoom, BreakoutRoom::STATUS_ASSISTANCE_RESET);
}
protected function setAssistanceRequest(Room $breakoutRoom, int $status): void {
if ($breakoutRoom->getObjectType() !== BreakoutRoom::PARENT_OBJECT_TYPE) {
throw new \InvalidArgumentException('room');
}
if ($breakoutRoom->getLobbyState() !== Webinary::LOBBY_NONE) {
throw new \InvalidArgumentException('room');
}
if (!in_array($status, [
BreakoutRoom::STATUS_ASSISTANCE_RESET,
BreakoutRoom::STATUS_ASSISTANCE_REQUESTED,
], true)) {
throw new \InvalidArgumentException('status');
}
$this->roomService->setBreakoutRoomStatus($breakoutRoom, $status);
}
public function startBreakoutRooms(Room $parent): void {
if ($parent->getBreakoutRoomMode() === BreakoutRoom::MODE_NOT_CONFIGURED) {
throw new \InvalidArgumentException('mode');
@ -300,10 +327,115 @@ class BreakoutRoomService {
$breakoutRooms = $this->manager->getMultipleRoomsByObject(BreakoutRoom::PARENT_OBJECT_TYPE, $parent->getToken());
foreach ($breakoutRooms as $breakoutRoom) {
$this->roomService->setLobby($breakoutRoom, Webinary::LOBBY_NON_MODERATORS, null);
if ($breakoutRoom->getBreakoutRoomStatus() === BreakoutRoom::STATUS_ASSISTANCE_REQUESTED) {
$this->roomService->setBreakoutRoomStatus($breakoutRoom, BreakoutRoom::STATUS_ASSISTANCE_RESET);
}
}
$this->roomService->setBreakoutRoomStatus($parent, BreakoutRoom::STATUS_STOPPED);
// FIXME missing to send the signaling messages so participants are moved back
}
public function switchBreakoutRoom(Room $parent, Participant $participant, string $targetToken): void {
if ($parent->getBreakoutRoomMode() !== BreakoutRoom::MODE_FREE) {
throw new \InvalidArgumentException('mode');
}
if ($parent->getBreakoutRoomStatus() !== BreakoutRoom::STATUS_STARTED) {
throw new \InvalidArgumentException('status');
}
if ($participant->hasModeratorPermissions()) {
// Moderators don't switch, they are part of all breakout rooms
throw new \InvalidArgumentException('moderator');
}
$attendee = $participant->getAttendee();
$breakoutRooms = $this->manager->getMultipleRoomsByObject(BreakoutRoom::PARENT_OBJECT_TYPE, $parent->getToken());
$foundTarget = false;
foreach ($breakoutRooms as $breakoutRoom) {
if ($targetToken === $breakoutRoom->getToken()) {
$foundTarget = true;
break;
}
}
if (!$foundTarget) {
throw new \InvalidArgumentException('target');
}
foreach ($breakoutRooms as $breakoutRoom) {
try {
$removeParticipant = $this->participantService->getParticipantByActor(
$breakoutRoom,
$attendee->getActorType(),
$attendee->getActorId()
);
if ($targetToken !== $breakoutRoom->getToken()) {
// Remove from all other breakout rooms
$this->participantService->removeAttendee(
$breakoutRoom,
$removeParticipant,
Room::PARTICIPANT_LEFT
);
}
} catch (ParticipantNotFoundException $e) {
if ($targetToken === $breakoutRoom->getToken()) {
// Join the target breakout room
$this->participantService->addUsers(
$breakoutRoom,
[
[
'actorType' => $attendee->getActorType(),
'actorId' => $attendee->getActorId(),
'displayName' => $attendee->getDisplayName(),
'participantType' => $attendee->getParticipantType(),
]
]
);
}
}
}
}
/**
* @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
}
}
return $rooms;
}
return $breakoutRooms;
}
}

2
lib/Service/RoomService.php

@ -633,6 +633,8 @@ class RoomService {
if (!in_array($status, [
BreakoutRoom::STATUS_STOPPED,
BreakoutRoom::STATUS_STARTED,
BreakoutRoom::STATUS_ASSISTANCE_RESET,
BreakoutRoom::STATUS_ASSISTANCE_REQUESTED,
], true)) {
return false;
}

74
tests/integration/features/bootstrap/FeatureContext.php

@ -282,6 +282,39 @@ class FeatureContext implements Context, SnippetAcceptingContext {
$this->assertRooms($rooms, $formData, $shouldOrder !== '');
}
/**
* @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
@ -2382,6 +2415,47 @@ class FeatureContext implements Context, SnippetAcceptingContext {
$this->assertStatusCode($this->response, $statusCode);
}
/**
* @Then /^user "([^"]*)" switches in room "([^"]*)" to breakout room "([^"]*)" with (\d+)(?: \((v1)\))?$/
*
* @param string $user
* @param string $identifier
* @param string $target
* @param string $statusCode
* @param string $apiVersion
*/
public function userSwitchesBreakoutRoom(string $user, string $identifier, string $target, string $statusCode, string $apiVersion = 'v1') {
$this->setCurrentUser($user);
$this->sendRequest(
'POST',
'/apps/spreed/api/' . $apiVersion . '/breakout-rooms/' . self::$identifierToToken[$identifier] . '/switch',
[
'target' => self::$identifierToToken[$target],
]
);
$this->assertStatusCode($this->response, $statusCode);
}
/**
* @Then /^user "([^"]*)" (requests assistance|cancels request for assistance) in room "([^"]*)" with (\d+)(?: \((v1)\))?$/
*
* @param string $user
* @param string $requestCancel
* @param string $identifier
* @param string $statusCode
* @param string $apiVersion
*/
public function userRequestsOrCancelsAssistanceInBreakoutRooms(string $user, string $requestCancel, string $identifier, string $statusCode, string $apiVersion = 'v1') {
$this->setCurrentUser($user);
$this->sendRequest(
$requestCancel === 'requests assistance' ? 'POST' : 'DELETE',
'/apps/spreed/api/' . $apiVersion . '/breakout-rooms/' . self::$identifierToToken[$identifier] . '/request-assistance'
);
$this->assertStatusCode($this->response, $statusCode);
}
/**
* @Then /^user "([^"]*)" sets setting "([^"]*)" to "([^"]*)" with (\d+)(?: \((v1)\))?$/
*

309
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 |
| roomType | 2 |
| roomName | class room |
And user "participant1" creates 0 manual breakout rooms for "class room" with 400 (v1)
Scenario: Invalid breakout room number in attendee map (low)
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)
@ -185,7 +185,7 @@ Feature: conversation/breakout-rooms
Scenario: Invalid breakout room number in attendee map (high)
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)
@ -197,7 +197,7 @@ Feature: conversation/breakout-rooms
Scenario: Breakout rooms are disabled
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)
@ -211,7 +211,7 @@ Feature: conversation/breakout-rooms
Scenario: Broadcast chat message to all breakout room
Given user "participant1" creates room "class room" (v4)
| roomType | 3 |
| roomType | 2 |
| roomName | class room |
And user "participant1" sees the following attendees in room "class room" with 200 (v4)
| actorType | actorId | participantType |
@ -219,10 +219,10 @@ Feature: conversation/breakout-rooms
When 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" broadcasts message "Hello rooms 1-3" to room "class room" with 201 (v1)
Then user "participant1" sees the following messages in room "Room 1" with 200
| room | actorType | actorId | actorDisplayName | message | messageParameters |
@ -236,20 +236,20 @@ Feature: conversation/breakout-rooms
Scenario: Can not broadcast chat message in a non-breakout room
Given user "participant1" creates room "room" (v4)
| roomType | 3 |
| roomType | 2 |
| roomName | class room |
And user "participant1" broadcasts message "Does not work" to room "room" with 400 (v1)
Scenario: Can not start in a non-breakout room
Given user "participant1" creates room "room" (v4)
| roomType | 3 |
| roomType | 2 |
| roomName | room |
And user "participant1" starts breakout rooms in room "room" with 400 (v1)
And user "participant1" stops breakout rooms in room "room" with 400 (v1)
Scenario: Moderator starts and stops breakout rooms
Given user "participant1" creates room "class room" (v4)
| roomType | 3 |
| roomType | 2 |
| roomName | class room |
And user "participant1" sees the following attendees in room "class room" with 200 (v4)
| actorType | actorId | participantType |
@ -257,10 +257,10 @@ Feature: conversation/breakout-rooms
When 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 | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 3 | class room | 0 | 2 | 0 |
| 3 | Room 1 | 1 | 0 | 0 |
| 3 | Room 2 | 1 | 0 | 0 |
| 3 | Room 3 | 1 | 0 | 0 |
| 2 | class room | 0 | 2 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
| 2 | Room 3 | 1 | 0 | 0 |
Then user "participant1" sees the following system messages in room "Room 1" with 200
| room | actorType | actorId | actorDisplayName | systemMessage |
| Room 1 | users | participant1 | participant1-displayname | conversation_created |
@ -273,10 +273,10 @@ Feature: conversation/breakout-rooms
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 3 | class room | 0 | 2 | 1 |
| 3 | Room 1 | 0 | 0 | 0 |
| 3 | Room 2 | 0 | 0 | 0 |
| 3 | Room 3 | 0 | 0 | 0 |
| 2 | class room | 0 | 2 | 1 |
| 2 | Room 1 | 0 | 0 | 0 |
| 2 | Room 2 | 0 | 0 | 0 |
| 2 | Room 3 | 0 | 0 | 0 |
Then user "participant1" sees the following system messages in room "Room 1" with 200
| room | actorType | actorId | actorDisplayName | systemMessage |
| Room 1 | users | participant1 | participant1-displayname | breakout_rooms_started |
@ -292,10 +292,10 @@ Feature: conversation/breakout-rooms
And user "participant1" stops breakout rooms in room "class room" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 3 | class room | 0 | 2 | 0 |
| 3 | Room 1 | 1 | 0 | 0 |
| 3 | Room 2 | 1 | 0 | 0 |
| 3 | Room 3 | 1 | 0 | 0 |
| 2 | class room | 0 | 2 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
| 2 | Room 3 | 1 | 0 | 0 |
Then user "participant1" sees the following system messages in room "Room 1" with 200
| room | actorType | actorId | actorDisplayName | systemMessage |
| Room 1 | users | participant1 | participant1-displayname | breakout_rooms_stopped |
@ -311,3 +311,184 @@ Feature: conversation/breakout-rooms
| Room 3 | users | participant1 | participant1-displayname | breakout_rooms_stopped |
| Room 3 | users | participant1 | participant1-displayname | breakout_rooms_started |
| Room 3 | users | participant1 | participant1-displayname | conversation_created |
Scenario: Request assistance and cancel it
Given user "participant1" creates room "class room" (v4)
| 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)
| actorType | actorId | participantType |
| users | participant1 | 1 |
| users | participant2 | 3 |
When user "participant1" creates 1 automatic breakout rooms for "class room" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 1 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
And user "participant2" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 1 | 1 |
| 2 | Room 1 | 0 | 0 | 0 |
And user "participant2" requests assistance in room "Room 1" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 1 | 1 |
| 2 | Room 1 | 0 | 0 | 2 |
And user "participant2" cancels request for assistance in room "Room 1" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 1 | 1 |
| 2 | Room 1 | 0 | 0 | 0 |
And user "participant2" requests assistance in room "Room 1" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 1 | 1 |
| 2 | Room 1 | 0 | 0 | 2 |
And user "participant1" cancels request for assistance in room "Room 1" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 1 | 1 |
| 2 | Room 1 | 0 | 0 | 0 |
Scenario: Teacher creates free breakout rooms
Given user "participant1" creates room "class room" (v4)
| roomType | 2 |
| roomName | class room |
And user "participant1" creates 2 free breakout rooms for "class room" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 3 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
Then user "participant2" sees the following breakout rooms for room "class room" with 404 (v4)
And user "participant1" adds user "participant2" to room "class room" with 200 (v4)
Then user "participant2" sees the following breakout rooms for room "class room" with 400 (v4)
Then user "participant1" sees the following breakout rooms for room "class room" with 200 (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
Then user "participant2" sees the following breakout rooms for room "class room" with 200 (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | Room 1 | 0 | 0 | 0 |
| 2 | Room 2 | 0 | 0 | 0 |
Scenario: Student can only get their own breakout room when non-free
Given user "participant1" creates room "class room" (v4)
| 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)
| actorType | actorId | participantType |
| users | participant1 | 1 |
| users | participant2 | 3 |
When user "participant1" creates 3 manual breakout rooms for "class room" with 200 (v1)
| users::participant2 | 0 |
Then user "participant1" is participant of the following rooms (v4)
| type | name |
| 2 | class room |
| 2 | Room 1 |
| 2 | Room 2 |
| 2 | Room 3 |
Then user "participant2" is participant of the following rooms (v4)
| type | name |
| 2 | class room |
| 2 | Room 1 |
Then user "participant1" sees the following breakout rooms for room "class room" with 200 (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
| 2 | Room 3 | 1 | 0 | 0 |
Then user "participant2" sees the following breakout rooms for room "class room" with 400 (v4)
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
Then user "participant2" sees the following breakout rooms for room "class room" with 200 (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | Room 1 | 0 | 0 | 0 |
Scenario: Teachers can not "switch" breakout rooms as they are in all of them
Given user "participant1" creates room "class room" (v4)
| roomType | 2 |
| roomName | class room |
And user "participant1" creates 2 free breakout rooms for "class room" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 3 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
When user "participant1" switches in room "class room" to breakout room "Room 1" with 400 (v1)
Scenario: Student switching breakout room in free selection
Given user "participant1" creates room "class room" (v4)
| roomType | 2 |
| roomName | class room |
And user "participant1" creates 2 free breakout rooms for "class room" with 200 (v1)
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 3 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
And user "participant1" adds user "participant2" to room "class room" with 200 (v4)
Then user "participant2" is participant of the following rooms (v4)
| type | name |
| 2 | class room |
When user "participant2" switches in room "class room" to breakout room "Room 1" with 400 (v1)
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
When user "participant2" switches in room "class room" to breakout room "Room 1" with 200 (v1)
Then user "participant2" is participant of the following rooms (v4)
| type | name |
| 2 | class room |
| 2 | Room 1 |
When user "participant2" switches in room "class room" to breakout room "Room 2" with 200 (v1)
Then user "participant2" is participant of the following rooms (v4)
| type | name |
| 2 | class room |
| 2 | Room 2 |
Scenario: Student can not switch on manual breakout rooms
Given user "participant1" creates room "class room" (v4)
| 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)
| actorType | actorId | participantType |
| users | participant1 | 1 |
| users | participant2 | 3 |
And user "participant1" creates 2 manual breakout rooms for "class room" with 200 (v1)
| users::participant2 | 0 |
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 2 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
Then user "participant2" is participant of the following rooms (v4)
| type | name |
| 2 | class room |
| 2 | Room 1 |
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
When user "participant2" switches in room "class room" to breakout room "Room 1" with 400 (v1)
Scenario: Student can not switch on automatic breakout rooms
Given user "participant1" creates room "class room" (v4)
| 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)
| actorType | actorId | participantType |
| users | participant1 | 1 |
| users | participant2 | 3 |
And user "participant1" creates 2 automatic breakout rooms for "class room" with 200 (v1)
| users::participant2 | 0 |
And user "participant1" is participant of the following rooms (v4)
| type | name | lobbyState | breakoutRoomMode | breakoutRoomStatus |
| 2 | class room | 0 | 1 | 0 |
| 2 | Room 1 | 1 | 0 | 0 |
| 2 | Room 2 | 1 | 0 | 0 |
Then user "participant2" is participant of the following rooms (v4)
| type | name |
| 2 | class room |
| 2 | Room 1 |
And user "participant1" starts breakout rooms in room "class room" with 200 (v1)
When user "participant2" switches in room "class room" to breakout room "Room 1" with 400 (v1)
Loading…
Cancel
Save