Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(api): Properly typed room-property updates - Part 2 #13173

Merged
merged 11 commits into from
Sep 11, 2024
36 changes: 26 additions & 10 deletions lib/Command/Room/TRoomCommand.php
nickvergessen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@
use OCA\Talk\Events\AAttendeeRemovedEvent;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Exceptions\RoomProperty\DescriptionException;
use OCA\Talk\Exceptions\RoomProperty\ListableException;
use OCA\Talk\Exceptions\RoomProperty\MessageExpirationException;
use OCA\Talk\Exceptions\RoomProperty\NameException;
use OCA\Talk\Exceptions\RoomProperty\PasswordException;
use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException;
use OCA\Talk\Exceptions\RoomProperty\TypeException;
use OCA\Talk\Manager;
use OCA\Talk\MatterbridgeManager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\RoomService;
use OCP\HintException;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
Expand Down Expand Up @@ -85,7 +90,7 @@ protected function validateRoomName(string $name): bool {
protected function setRoomDescription(Room $room, string $description): void {
try {
$this->roomService->setDescription($room, $description);
} catch (\LengthException $e) {
} catch (DescriptionException $e) {
throw new InvalidArgumentException('Invalid room description.');
}
}
Expand All @@ -101,7 +106,9 @@ protected function setRoomPublic(Room $room, bool $public): void {
return;
}

if (!$this->roomService->setType($room, $public ? Room::TYPE_PUBLIC : Room::TYPE_GROUP)) {
try {
$this->roomService->setType($room, $public ? Room::TYPE_PUBLIC : Room::TYPE_GROUP);
} catch (TypeException) {
throw new InvalidArgumentException('Unable to change room type.');
}
}
Expand All @@ -117,7 +124,9 @@ protected function setRoomReadOnly(Room $room, bool $readOnly): void {
return;
}

if (!$this->roomService->setReadOnly($room, $readOnly ? Room::READ_ONLY : Room::READ_WRITE)) {
try {
$this->roomService->setReadOnly($room, $readOnly ? Room::READ_ONLY : Room::READ_WRITE);
} catch (ReadOnlyException) {
throw new InvalidArgumentException('Unable to change room state.');
}
}
Expand All @@ -133,7 +142,9 @@ protected function setRoomListable(Room $room, int $listable): void {
return;
}

if (!$this->roomService->setListable($room, $listable)) {
try {
$this->roomService->setListable($room, $listable);
} catch (ListableException) {
throw new InvalidArgumentException('Unable to change room state.');
}
}
Expand All @@ -154,11 +165,12 @@ protected function setRoomPassword(Room $room, string $password): void {
}

try {
if (!$this->roomService->setPassword($room, $password)) {
throw new InvalidArgumentException('Unable to change room password.');
$this->roomService->setPassword($room, $password);
} catch (PasswordException $e) {
if ($e->getReason() === PasswordException::REASON_VALUE) {
throw new InvalidArgumentException($e->getHint());
}
} catch (HintException $e) {
throw new InvalidArgumentException($e->getHint());
throw new InvalidArgumentException('Unable to change room password.');
}
}

Expand Down Expand Up @@ -396,6 +408,10 @@ protected function completeParticipantValues(CompletionContext $context): array
}

protected function setMessageExpiration(Room $room, int $seconds): void {
$this->roomService->setMessageExpiration($room, $seconds);
try {
$this->roomService->setMessageExpiration($room, $seconds);
} catch (MessageExpirationException) {
throw new InvalidArgumentException('Unable to change message expiration.');
}
}
}
89 changes: 46 additions & 43 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException;
use OCA\Talk\Exceptions\RoomProperty\DescriptionException;
use OCA\Talk\Exceptions\RoomProperty\ListableException;
use OCA\Talk\Exceptions\RoomProperty\LobbyException;
use OCA\Talk\Exceptions\RoomProperty\MentionPermissionsException;
use OCA\Talk\Exceptions\RoomProperty\MessageExpirationException;
use OCA\Talk\Exceptions\RoomProperty\NameException;
use OCA\Talk\Exceptions\RoomProperty\PasswordException;
use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException;
use OCA\Talk\Exceptions\RoomProperty\RecordingConsentException;
use OCA\Talk\Exceptions\RoomProperty\SipConfigurationException;
use OCA\Talk\Exceptions\RoomProperty\TypeException;
use OCA\Talk\Exceptions\UnauthorizedException;
use OCA\Talk\Federation\Authenticator;
use OCA\Talk\Federation\FederationManager;
Expand Down Expand Up @@ -63,7 +70,6 @@
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudIdManager;
use OCP\HintException;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
Expand Down Expand Up @@ -801,22 +807,18 @@ public function renameRoom(string $roomName): DataResponse {
* Update the description of a room
*
* @param string $description New description
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'type'|'value'}, array{}>
*
* 200: Description updated successfully
* 400: Updating description is not possible
*/
#[PublicPage]
#[RequireModeratorParticipant]
public function setDescription(string $description): DataResponse {
if ($this->room->getType() === Room::TYPE_ONE_TO_ONE || $this->room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

try {
$this->roomService->setDescription($this->room, $description);
} catch (\LengthException $exception) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
} catch (DescriptionException $e) {
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
Expand Down Expand Up @@ -1187,8 +1189,10 @@ public function addParticipantToRoom(string $newParticipant, string $source = 'u
$this->participantService->addCircle($this->room, $circle, $participants);
} elseif ($source === 'emails') {
$data = [];
if ($this->roomService->setType($this->room, Room::TYPE_PUBLIC)) {
try {
$this->roomService->setType($this->room, Room::TYPE_PUBLIC);
$data = ['type' => $this->room->getType()];
} catch (TypeException) {
}

try {
Expand Down Expand Up @@ -1402,16 +1406,18 @@ public function removeAttendeeFromRoom(int $attendeeId): DataResponse {
/**
* Allowed guests to join conversation
*
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value'}, array{}>
*
* 200: Allowed guests successfully
* 400: Allowing guests is not possible
*/
#[NoAdminRequired]
#[RequireLoggedInModeratorParticipant]
public function makePublic(): DataResponse {
if (!$this->roomService->setType($this->room, Room::TYPE_PUBLIC)) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
try {
$this->roomService->setType($this->room, Room::TYPE_PUBLIC);
} catch (TypeException $e) {
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
Expand All @@ -1420,16 +1426,18 @@ public function makePublic(): DataResponse {
/**
* Disallowed guests to join conversation
*
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value'}, array{}>
*
* 200: Room unpublished Disallowing guests successfully
* 400: Disallowing guests is not possible
*/
#[NoAdminRequired]
#[RequireLoggedInModeratorParticipant]
public function makePrivate(): DataResponse {
if (!$this->roomService->setType($this->room, Room::TYPE_GROUP)) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
try {
$this->roomService->setType($this->room, Room::TYPE_GROUP);
} catch (TypeException $e) {
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
Expand All @@ -1440,16 +1448,18 @@ public function makePrivate(): DataResponse {
*
* @param 0|1 $state New read-only state
* @psalm-param Room::READ_* $state
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'type'|'value'}, array{}>
*
* 200: Read-only state updated successfully
* 400: Updating read-only state is not possible
*/
#[NoAdminRequired]
#[RequireModeratorParticipant]
public function setReadOnly(int $state): DataResponse {
if (!$this->roomService->setReadOnly($this->room, $state)) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
try {
$this->roomService->setReadOnly($this->room, $state);
} catch (ReadOnlyException $e) {
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

if ($state === Room::READ_ONLY) {
Expand All @@ -1469,16 +1479,18 @@ public function setReadOnly(int $state): DataResponse {
*
* @param 0|1|2 $scope Scope where the room is listable
* @psalm-param Room::LISTABLE_* $scope
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value'}, array{}>
*
* 200: Made room listable successfully
* 400: Making room listable is not possible
*/
#[NoAdminRequired]
#[RequireModeratorParticipant]
public function setListable(int $scope): DataResponse {
if (!$this->roomService->setListable($this->room, $scope)) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
try {
$this->roomService->setListable($this->room, $scope);
} catch (ListableException $e) {
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
Expand All @@ -1489,7 +1501,7 @@ public function setListable(int $scope): DataResponse {
*
* @param 0|1 $mentionPermissions New mention permissions
* @psalm-param Room::MENTION_PERMISSIONS_* $mentionPermissions
* @return DataResponse<Http::STATUS_OK, TalkRoom, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, TalkRoom, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value'}, array{}>
*
* 200: Permissions updated successfully
* 400: Updating permissions is not possible
Expand All @@ -1499,8 +1511,8 @@ public function setListable(int $scope): DataResponse {
public function setMentionPermissions(int $mentionPermissions): DataResponse {
try {
$this->roomService->setMentionPermissions($this->room, $mentionPermissions);
} catch (\InvalidArgumentException) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
} catch (MentionPermissionsException $e) {
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

return new DataResponse($this->formatRoom($this->room, $this->participant));
Expand All @@ -1510,27 +1522,22 @@ public function setMentionPermissions(int $mentionPermissions): DataResponse {
* Set a password for a room
*
* @param string $password New password
* @return DataResponse<Http::STATUS_OK|Http::STATUS_FORBIDDEN, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{message?: string}, array{}>
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value', message?: string}, array{}>
*
* 200: Password set successfully
* 400: Setting password is not possible
* 403: Setting password is not allowed
*/
#[PublicPage]
#[RequireModeratorParticipant]
public function setPassword(string $password): DataResponse {
if ($this->room->getType() !== Room::TYPE_PUBLIC) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}

try {
if (!$this->roomService->setPassword($this->room, $password)) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->roomService->setPassword($this->room, $password);
} catch (PasswordException $e) {
$data = ['error' => $e->getReason()];
if ($e->getHint() !== '') {
$data['message'] = $e->getHint();
}
} catch (HintException $e) {
return new DataResponse([
'message' => $e->getHint(),
], Http::STATUS_BAD_REQUEST);
return new DataResponse($data, Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
Expand Down Expand Up @@ -2347,22 +2354,18 @@ public function resendInvitations(?int $attendeeId): DataResponse {
*
* @param int $seconds New time
* @psalm-param non-negative-int $seconds
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error?: string}, array{}>
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value'}, array{}>
*
* 200: Message expiration time updated successfully
* 400: Updating message expiration time is not possible
*/
#[PublicPage]
#[RequireModeratorParticipant]
public function setMessageExpiration(int $seconds): DataResponse {
if ($seconds < 0) {
return new DataResponse(['error' => 'seconds'], Http::STATUS_BAD_REQUEST);
}

try {
$this->roomService->setMessageExpiration($this->room, $seconds);
} catch (\InvalidArgumentException $exception) {
return new DataResponse(['error' => $exception->getMessage()], Http::STATUS_BAD_REQUEST);
} catch (MessageExpirationException $e) {
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
Expand Down
29 changes: 29 additions & 0 deletions lib/Exceptions/RoomProperty/AvatarException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Talk\Exceptions\RoomProperty;

class AvatarException extends \InvalidArgumentException {
public const REASON_TYPE = 'type';

/**
* @param self::REASON_* $reason
*/
public function __construct(
protected string $reason,
) {
parent::__construct($reason);
}

/**
* @return self::REASON_*
*/
public function getReason(): string {
return $this->reason;
}
}
29 changes: 29 additions & 0 deletions lib/Exceptions/RoomProperty/BreakoutRoomModeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Talk\Exceptions\RoomProperty;

class BreakoutRoomModeException extends \InvalidArgumentException {
public const REASON_VALUE = 'value';

/**
* @param self::REASON_* $reason
*/
public function __construct(
protected string $reason,
) {
parent::__construct($reason);
}

/**
* @return self::REASON_*
*/
public function getReason(): string {
return $this->reason;
}
}
Loading
Loading