From d73e3e09ce758724ca56c9cbf1962b29068f50cf Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 09:28:34 +0200 Subject: [PATCH 01/11] fix(api): Properly typed callRecording update Signed-off-by: Joas Schilling --- .../RoomProperty/CallRecordingException.php | 34 +++++++++++++++++++ lib/Service/RoomService.php | 14 ++++---- 2 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/CallRecordingException.php diff --git a/lib/Exceptions/RoomProperty/CallRecordingException.php b/lib/Exceptions/RoomProperty/CallRecordingException.php new file mode 100644 index 00000000000..af75d371fd1 --- /dev/null +++ b/lib/Exceptions/RoomProperty/CallRecordingException.php @@ -0,0 +1,34 @@ +reason; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index a30baefed7f..32fab3a5692 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -27,6 +27,7 @@ use OCA\Talk\Events\RoomPasswordVerifyEvent; use OCA\Talk\Events\RoomSyncedEvent; use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Exceptions\RoomProperty\CallRecordingException; use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; use OCA\Talk\Exceptions\RoomProperty\LobbyException; use OCA\Talk\Exceptions\RoomProperty\NameException; @@ -437,18 +438,17 @@ public function setAvatar(Room $room, string $avatar): bool { * @param integer $status 0 none|1 video|2 audio * @param Participant|null $participant the Participant that changed the * state, null for the current user - * @throws InvalidArgumentException When the status is invalid, not Room::RECORDING_* - * @throws InvalidArgumentException When trying to start + * @throws CallRecordingException */ public function setCallRecording(Room $room, int $status = Room::RECORDING_NONE, ?Participant $participant = null): void { $syncFederatedRoom = $room->getRemoteServer() && $room->getRemoteToken(); if (!$syncFederatedRoom && !$this->config->isRecordingEnabled() && $status !== Room::RECORDING_NONE) { - throw new InvalidArgumentException('config'); + throw new CallRecordingException(CallRecordingException::REASON_CONFIG); } $availableRecordingStatus = [Room::RECORDING_NONE, Room::RECORDING_VIDEO, Room::RECORDING_AUDIO, Room::RECORDING_VIDEO_STARTING, Room::RECORDING_AUDIO_STARTING, Room::RECORDING_FAILED]; - if (!in_array($status, $availableRecordingStatus)) { - throw new InvalidArgumentException('status'); + if (!in_array($status, $availableRecordingStatus, true)) { + throw new CallRecordingException(CallRecordingException::REASON_STATUS); } $oldStatus = $room->getCallRecording(); @@ -1046,8 +1046,8 @@ public function syncPropertiesFromHostRoom(Room $local, array $host): void { try { $this->setCallRecording($local, $host['callRecording']); $changed[] = ARoomModifiedEvent::PROPERTY_CALL_RECORDING; - } catch (\InvalidArgumentException $e) { - $this->logger->error('An error (' . $e->getMessage() . ') occurred while trying to sync callRecording of ' . $local->getId() . ' to ' . $host['callRecording'], ['exception' => $e]); + } catch (CallRecordingException $e) { + $this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync callRecording of ' . $local->getId() . ' to ' . $host['callRecording'], ['exception' => $e]); } } if (isset($host['defaultPermissions']) && $host['defaultPermissions'] !== $local->getDefaultPermissions()) { From 7c12b9360996593b7be357d169362140dc3822e6 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 09:42:02 +0200 Subject: [PATCH 02/11] fix(api): Properly typed type update Signed-off-by: Joas Schilling --- lib/Command/Room/TRoomCommand.php | 5 ++- lib/Controller/RoomController.php | 21 ++++++++----- lib/Exceptions/RoomProperty/TypeException.php | 31 +++++++++++++++++++ lib/Service/RoomService.php | 31 +++++++++---------- 4 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/TypeException.php diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php index 249b92beece..eed84fa8325 100644 --- a/lib/Command/Room/TRoomCommand.php +++ b/lib/Command/Room/TRoomCommand.php @@ -13,6 +13,7 @@ use OCA\Talk\Exceptions\ParticipantNotFoundException; use OCA\Talk\Exceptions\RoomNotFoundException; use OCA\Talk\Exceptions\RoomProperty\NameException; +use OCA\Talk\Exceptions\RoomProperty\TypeException; use OCA\Talk\Manager; use OCA\Talk\MatterbridgeManager; use OCA\Talk\Model\Attendee; @@ -101,7 +102,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.'); } } diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 0fd609102cf..2e4141527b2 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -22,6 +22,7 @@ use OCA\Talk\Exceptions\RoomProperty\NameException; 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; @@ -1187,8 +1188,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 { @@ -1402,7 +1405,7 @@ public function removeAttendeeFromRoom(int $attendeeId): DataResponse { /** * Allowed guests to join conversation * - * @return DataResponse, array{}> + * @return DataResponse, array{}>|DataResponse * * 200: Allowed guests successfully * 400: Allowing guests is not possible @@ -1410,8 +1413,10 @@ public function removeAttendeeFromRoom(int $attendeeId): DataResponse { #[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(); @@ -1420,7 +1425,7 @@ public function makePublic(): DataResponse { /** * Disallowed guests to join conversation * - * @return DataResponse, array{}> + * @return DataResponse, array{}>|DataResponse * * 200: Room unpublished Disallowing guests successfully * 400: Disallowing guests is not possible @@ -1428,8 +1433,10 @@ public function makePublic(): DataResponse { #[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(); diff --git a/lib/Exceptions/RoomProperty/TypeException.php b/lib/Exceptions/RoomProperty/TypeException.php new file mode 100644 index 00000000000..8a9e62dca92 --- /dev/null +++ b/lib/Exceptions/RoomProperty/TypeException.php @@ -0,0 +1,31 @@ +reason; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 32fab3a5692..27508c935ca 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -33,6 +33,7 @@ use OCA\Talk\Exceptions\RoomProperty\NameException; use OCA\Talk\Exceptions\RoomProperty\RecordingConsentException; use OCA\Talk\Exceptions\RoomProperty\SipConfigurationException; +use OCA\Talk\Exceptions\RoomProperty\TypeException; use OCA\Talk\Manager; use OCA\Talk\Model\Attendee; use OCA\Talk\Model\BreakoutRoom; @@ -471,40 +472,40 @@ public function setCallRecording(Room $room, int $status = Room::RECORDING_NONE, * @param Room $room * @param int $newType Currently it is only allowed to change between `Room::TYPE_GROUP` and `Room::TYPE_PUBLIC` * @param bool $allowSwitchingOneToOne Allows additionally to change the type from `Room::TYPE_ONE_TO_ONE` to `Room::TYPE_ONE_TO_ONE_FORMER` - * @return bool True when the change was valid, false otherwise + * @throws TypeException */ - public function setType(Room $room, int $newType, bool $allowSwitchingOneToOne = false): bool { + public function setType(Room $room, int $newType, bool $allowSwitchingOneToOne = false): void { $oldType = $room->getType(); if ($oldType === $newType) { - return true; + return; } if (!$allowSwitchingOneToOne && $oldType === Room::TYPE_ONE_TO_ONE) { - return false; + throw new TypeException(TypeException::REASON_TYPE); } if ($oldType === Room::TYPE_ONE_TO_ONE_FORMER) { - return false; + throw new TypeException(TypeException::REASON_TYPE); } if ($oldType === Room::TYPE_NOTE_TO_SELF) { - return false; + throw new TypeException(TypeException::REASON_TYPE); } if (!in_array($newType, [Room::TYPE_GROUP, Room::TYPE_PUBLIC, Room::TYPE_ONE_TO_ONE_FORMER], true)) { - return false; + throw new TypeException(TypeException::REASON_VALUE); } if ($newType === Room::TYPE_ONE_TO_ONE_FORMER && $oldType !== Room::TYPE_ONE_TO_ONE) { - return false; + throw new TypeException(TypeException::REASON_VALUE); } if ($room->getBreakoutRoomMode() !== BreakoutRoom::MODE_NOT_CONFIGURED) { - return false; + throw new TypeException(TypeException::REASON_BREAKOUT_ROOM); } if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE) { - return false; + throw new TypeException(TypeException::REASON_BREAKOUT_ROOM); } $event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_TYPE, $newType, $oldType); @@ -529,8 +530,6 @@ public function setType(Room $room, int $newType, bool $allowSwitchingOneToOne = $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_TYPE, $newType, $oldType); $this->dispatcher->dispatchTyped($event); - - return true; } /** @@ -1015,11 +1014,11 @@ public function syncPropertiesFromHostRoom(Room $local, array $host): void { /** @var array $changed */ $changed = []; if (isset($host['type']) && $host['type'] !== $local->getType()) { - $success = $this->setType($local, $host['type']); - if (!$success) { - $this->logger->error('An error occurred while trying to sync type of ' . $local->getId() . ' to ' . $host['type']); - } else { + try { + $this->setType($local, $host['type']); $changed[] = ARoomModifiedEvent::PROPERTY_TYPE; + } catch (TypeException $e) { + $this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync n type of ' . $local->getId() . ' to ' . $host['type'], ['exception' => $e]); } } if (isset($host['name']) && $host['name'] !== $local->getName()) { From 55a3a71933b6eda8574f0a2c0624624622a91f88 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 09:51:27 +0200 Subject: [PATCH 03/11] fix(api): Properly typed readOnly update Signed-off-by: Joas Schilling --- lib/Command/Room/TRoomCommand.php | 5 +++- lib/Controller/RoomController.php | 9 ++++-- .../RoomProperty/ReadOnlyException.php | 30 +++++++++++++++++++ lib/Service/RoomService.php | 21 +++++++------ 4 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/ReadOnlyException.php diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php index eed84fa8325..6c46cbeafd3 100644 --- a/lib/Command/Room/TRoomCommand.php +++ b/lib/Command/Room/TRoomCommand.php @@ -13,6 +13,7 @@ use OCA\Talk\Exceptions\ParticipantNotFoundException; use OCA\Talk\Exceptions\RoomNotFoundException; use OCA\Talk\Exceptions\RoomProperty\NameException; +use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; use OCA\Talk\Exceptions\RoomProperty\TypeException; use OCA\Talk\Manager; use OCA\Talk\MatterbridgeManager; @@ -120,7 +121,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.'); } } diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 2e4141527b2..50b5b6ec097 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -20,6 +20,7 @@ use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; use OCA\Talk\Exceptions\RoomProperty\LobbyException; use OCA\Talk\Exceptions\RoomProperty\NameException; +use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; use OCA\Talk\Exceptions\RoomProperty\RecordingConsentException; use OCA\Talk\Exceptions\RoomProperty\SipConfigurationException; use OCA\Talk\Exceptions\RoomProperty\TypeException; @@ -1447,7 +1448,7 @@ public function makePrivate(): DataResponse { * * @param 0|1 $state New read-only state * @psalm-param Room::READ_* $state - * @return DataResponse, array{}> + * @return DataResponse, array{}>|DataResponse * * 200: Read-only state updated successfully * 400: Updating read-only state is not possible @@ -1455,8 +1456,10 @@ public function makePrivate(): DataResponse { #[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) { diff --git a/lib/Exceptions/RoomProperty/ReadOnlyException.php b/lib/Exceptions/RoomProperty/ReadOnlyException.php new file mode 100644 index 00000000000..91b2d6b4ec6 --- /dev/null +++ b/lib/Exceptions/RoomProperty/ReadOnlyException.php @@ -0,0 +1,30 @@ +reason; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 27508c935ca..09949218b99 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -31,6 +31,7 @@ use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; use OCA\Talk\Exceptions\RoomProperty\LobbyException; use OCA\Talk\Exceptions\RoomProperty\NameException; +use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; use OCA\Talk\Exceptions\RoomProperty\RecordingConsentException; use OCA\Talk\Exceptions\RoomProperty\SipConfigurationException; use OCA\Talk\Exceptions\RoomProperty\TypeException; @@ -538,23 +539,23 @@ public function setType(Room $room, int $newType, bool $allowSwitchingOneToOne = * `Room::READ_ONLY` and `Room::READ_WRITE` * Also it's only allowed on rooms of type * `Room::TYPE_GROUP` and `Room::TYPE_PUBLIC` - * @return bool True when the change was valid, false otherwise + * @throws ReadOnlyException */ - public function setReadOnly(Room $room, int $newState): bool { + public function setReadOnly(Room $room, int $newState): void { $oldState = $room->getReadOnly(); if ($newState === $oldState) { - return true; + return; } if (!in_array($room->getType(), [Room::TYPE_GROUP, Room::TYPE_PUBLIC, Room::TYPE_CHANGELOG], true)) { if ($newState !== Room::READ_ONLY || $room->getType() !== Room::TYPE_ONE_TO_ONE_FORMER) { // Allowed for the automated conversation of one-to-one chats to read only former - return false; + throw new ReadOnlyException(ReadOnlyException::REASON_TYPE); } } if (!in_array($newState, [Room::READ_ONLY, Room::READ_WRITE], true)) { - return false; + throw new ReadOnlyException(ReadOnlyException::REASON_VALUE); } $event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_READ_ONLY, $newState, $oldState); @@ -570,8 +571,6 @@ public function setReadOnly(Room $room, int $newState): bool { $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_READ_ONLY, $newState, $oldState); $this->dispatcher->dispatchTyped($event); - - return true; } /** @@ -1114,11 +1113,11 @@ public function syncPropertiesFromHostRoom(Room $local, array $host): void { } } if (isset($host['readOnly']) && $host['readOnly'] !== $local->getReadOnly()) { - $success = $this->setReadOnly($local, $host['readOnly']); - if (!$success) { - $this->logger->error('An error occurred while trying to sync readOnly of ' . $local->getId() . ' to ' . $host['readOnly']); - } else { + try { + $this->setReadOnly($local, $host['readOnly']); $changed[] = ARoomModifiedEvent::PROPERTY_READ_ONLY; + } catch (ReadOnlyException $e) { + $this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync readOnly of ' . $local->getId() . ' to ' . $host['readOnly'], ['exception' => $e]); } } if (isset($host['recordingConsent']) && $host['recordingConsent'] !== $local->getRecordingConsent()) { From 4f82ee9a31893ef98de22bac4069520618177766 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 09:55:48 +0200 Subject: [PATCH 04/11] fix(api): Properly typed listable update Signed-off-by: Joas Schilling --- lib/Command/Room/TRoomCommand.php | 5 ++- lib/Controller/RoomController.php | 9 ++++-- .../RoomProperty/ListableException.php | 31 +++++++++++++++++++ lib/Service/RoomService.php | 16 +++++----- 4 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/ListableException.php diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php index 6c46cbeafd3..bda8969090d 100644 --- a/lib/Command/Room/TRoomCommand.php +++ b/lib/Command/Room/TRoomCommand.php @@ -12,6 +12,7 @@ use OCA\Talk\Events\AAttendeeRemovedEvent; use OCA\Talk\Exceptions\ParticipantNotFoundException; use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Exceptions\RoomProperty\ListableException; use OCA\Talk\Exceptions\RoomProperty\NameException; use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; use OCA\Talk\Exceptions\RoomProperty\TypeException; @@ -139,7 +140,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.'); } } diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 50b5b6ec097..a9cfd3240a8 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -18,6 +18,7 @@ use OCA\Talk\Exceptions\ParticipantNotFoundException; use OCA\Talk\Exceptions\RoomNotFoundException; use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; +use OCA\Talk\Exceptions\RoomProperty\ListableException; use OCA\Talk\Exceptions\RoomProperty\LobbyException; use OCA\Talk\Exceptions\RoomProperty\NameException; use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; @@ -1479,7 +1480,7 @@ public function setReadOnly(int $state): DataResponse { * * @param 0|1|2 $scope Scope where the room is listable * @psalm-param Room::LISTABLE_* $scope - * @return DataResponse, array{}> + * @return DataResponse, array{}>|DataResponse * * 200: Made room listable successfully * 400: Making room listable is not possible @@ -1487,8 +1488,10 @@ public function setReadOnly(int $state): DataResponse { #[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(); diff --git a/lib/Exceptions/RoomProperty/ListableException.php b/lib/Exceptions/RoomProperty/ListableException.php new file mode 100644 index 00000000000..3728137b1fa --- /dev/null +++ b/lib/Exceptions/RoomProperty/ListableException.php @@ -0,0 +1,31 @@ +reason; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 09949218b99..0530f4b9b16 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -29,6 +29,7 @@ use OCA\Talk\Exceptions\RoomNotFoundException; use OCA\Talk\Exceptions\RoomProperty\CallRecordingException; use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; +use OCA\Talk\Exceptions\RoomProperty\ListableException; use OCA\Talk\Exceptions\RoomProperty\LobbyException; use OCA\Talk\Exceptions\RoomProperty\NameException; use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; @@ -578,20 +579,21 @@ public function setReadOnly(Room $room, int $newState): void { * @param int $newState New listable scope from self::LISTABLE_* * Also it's only allowed on rooms of type * `Room::TYPE_GROUP` and `Room::TYPE_PUBLIC` - * @return bool True when the change was valid, false otherwise + * @psalm-param Room::LISTABLE_* $newState + * @throws ListableException */ - public function setListable(Room $room, int $newState): bool { + public function setListable(Room $room, int $newState): void { $oldState = $room->getListable(); if ($newState === $oldState) { - return true; + return; } if (!in_array($room->getType(), [Room::TYPE_GROUP, Room::TYPE_PUBLIC], true)) { - return false; + throw new ListableException(ListableException::REASON_TYPE); } if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE) { - return false; + throw new ListableException(ListableException::REASON_BREAKOUT_ROOM); } if (!in_array($newState, [ @@ -599,7 +601,7 @@ public function setListable(Room $room, int $newState): bool { Room::LISTABLE_USERS, Room::LISTABLE_ALL, ], true)) { - return false; + throw new ListableException(ListableException::REASON_VALUE); } $event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_LISTABLE, $newState, $oldState); @@ -615,8 +617,6 @@ public function setListable(Room $room, int $newState): bool { $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_LISTABLE, $newState, $oldState); $this->dispatcher->dispatchTyped($event); - - return true; } /** From ee42d4417162ed813a4250ee1ef2ed6f6508cbc3 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 10:05:58 +0200 Subject: [PATCH 05/11] fix(api): Properly typed mentionPermissions update Signed-off-by: Joas Schilling --- lib/Controller/RoomController.php | 7 +++-- .../MentionPermissionsException.php | 31 +++++++++++++++++++ .../CloudFederationProviderTalk.php | 1 + lib/Service/RoomService.php | 16 +++++----- 4 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/MentionPermissionsException.php diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index a9cfd3240a8..e16eb2bc594 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -20,6 +20,7 @@ use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; use OCA\Talk\Exceptions\RoomProperty\ListableException; use OCA\Talk\Exceptions\RoomProperty\LobbyException; +use OCA\Talk\Exceptions\RoomProperty\MentionPermissionsException; use OCA\Talk\Exceptions\RoomProperty\NameException; use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; use OCA\Talk\Exceptions\RoomProperty\RecordingConsentException; @@ -1502,7 +1503,7 @@ public function setListable(int $scope): DataResponse { * * @param 0|1 $mentionPermissions New mention permissions * @psalm-param Room::MENTION_PERMISSIONS_* $mentionPermissions - * @return DataResponse|DataResponse, array{}> + * @return DataResponse|DataResponse * * 200: Permissions updated successfully * 400: Updating permissions is not possible @@ -1512,8 +1513,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)); diff --git a/lib/Exceptions/RoomProperty/MentionPermissionsException.php b/lib/Exceptions/RoomProperty/MentionPermissionsException.php new file mode 100644 index 00000000000..d4e17287164 --- /dev/null +++ b/lib/Exceptions/RoomProperty/MentionPermissionsException.php @@ -0,0 +1,31 @@ +reason; + } +} diff --git a/lib/Federation/CloudFederationProviderTalk.php b/lib/Federation/CloudFederationProviderTalk.php index 2e04da6605f..36dea0bf39d 100644 --- a/lib/Federation/CloudFederationProviderTalk.php +++ b/lib/Federation/CloudFederationProviderTalk.php @@ -377,6 +377,7 @@ private function roomModified(int $remoteAttendeeId, array $notification): array $dateTime = !empty($notification['dateTime']) ? \DateTime::createFromFormat('U', $notification['dateTime']) : null; $this->roomService->setLobby($room, $notification['newValue'], $dateTime, $notification['timerReached'] ?? false); } elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS) { + /** @psalm-suppress InvalidArgument */ $this->roomService->setMentionPermissions($room, $notification['newValue']); } elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION) { $this->roomService->setMessageExpiration($room, $notification['newValue']); diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 0530f4b9b16..88c5146cc95 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -31,6 +31,7 @@ use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; use OCA\Talk\Exceptions\RoomProperty\ListableException; use OCA\Talk\Exceptions\RoomProperty\LobbyException; +use OCA\Talk\Exceptions\RoomProperty\MentionPermissionsException; use OCA\Talk\Exceptions\RoomProperty\NameException; use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; use OCA\Talk\Exceptions\RoomProperty\RecordingConsentException; @@ -621,8 +622,9 @@ public function setListable(Room $room, int $newState): void { /** * @param Room $room - * @param int $newState New mention permissions from self::MENTION_PERMISSIONS_* - * @throws \InvalidArgumentException When the room type, state or breakout rooms where invalid + * @param int $newState New mention permissions from Room::MENTION_PERMISSIONS_* + * @psalm-param Room::MENTION_PERMISSIONS_* $newState + * @throws MentionPermissionsException */ public function setMentionPermissions(Room $room, int $newState): void { $oldState = $room->getMentionPermissions(); @@ -631,15 +633,15 @@ public function setMentionPermissions(Room $room, int $newState): void { } if (!in_array($room->getType(), [Room::TYPE_GROUP, Room::TYPE_PUBLIC], true)) { - throw new \InvalidArgumentException('type'); + throw new MentionPermissionsException(MentionPermissionsException::REASON_TYPE); } if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE) { - throw new \InvalidArgumentException('breakout-room'); + throw new MentionPermissionsException(MentionPermissionsException::REASON_BREAKOUT_ROOM); } if (!in_array($newState, [Room::MENTION_PERMISSIONS_EVERYONE, Room::MENTION_PERMISSIONS_MODERATORS], true)) { - throw new \InvalidArgumentException('state'); + throw new MentionPermissionsException(MentionPermissionsException::REASON_VALUE); } $event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS, $newState, $oldState); @@ -1100,8 +1102,8 @@ public function syncPropertiesFromHostRoom(Room $local, array $host): void { try { $this->setMentionPermissions($local, $host['mentionPermissions']); $changed[] = ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS; - } catch (\InvalidArgumentException $e) { - $this->logger->error('An error (' . $e->getMessage() . ') occurred while trying to sync mentionPermissions of ' . $local->getId() . ' to ' . $host['mentionPermissions'], ['exception' => $e]); + } catch (MentionPermissionsException $e) { + $this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync mentionPermissions of ' . $local->getId() . ' to ' . $host['mentionPermissions'], ['exception' => $e]); } } if (isset($host['messageExpiration']) && $host['messageExpiration'] !== $local->getMessageExpiration()) { From b113df8cd4964a1502f842f7e85ed9f04c0be18c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 10:17:01 +0200 Subject: [PATCH 06/11] fix(api): Properly typed description update Signed-off-by: Joas Schilling --- lib/Command/Room/TRoomCommand.php | 3 +- lib/Controller/RoomController.php | 11 +++---- .../RoomProperty/DescriptionException.php | 30 +++++++++++++++++ lib/Service/RoomService.php | 32 +++++++++---------- 4 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/DescriptionException.php diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php index bda8969090d..c7054ceb98b 100644 --- a/lib/Command/Room/TRoomCommand.php +++ b/lib/Command/Room/TRoomCommand.php @@ -12,6 +12,7 @@ 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\NameException; use OCA\Talk\Exceptions\RoomProperty\ReadOnlyException; @@ -88,7 +89,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.'); } } diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index e16eb2bc594..f0b1ac7aef8 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -18,6 +18,7 @@ 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; @@ -805,7 +806,7 @@ public function renameRoom(string $roomName): DataResponse { * Update the description of a room * * @param string $description New description - * @return DataResponse, array{}> + * @return DataResponse, array{}>|DataResponse * * 200: Description updated successfully * 400: Updating description is not possible @@ -813,14 +814,10 @@ public function renameRoom(string $roomName): DataResponse { #[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(); diff --git a/lib/Exceptions/RoomProperty/DescriptionException.php b/lib/Exceptions/RoomProperty/DescriptionException.php new file mode 100644 index 00000000000..0d6a330cc2e --- /dev/null +++ b/lib/Exceptions/RoomProperty/DescriptionException.php @@ -0,0 +1,30 @@ +reason; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 88c5146cc95..31c38ed90e6 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -29,6 +29,7 @@ use OCA\Talk\Exceptions\RoomNotFoundException; use OCA\Talk\Exceptions\RoomProperty\CallRecordingException; 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; @@ -678,19 +679,22 @@ public function setAssignedSignalingServer(Room $room, ?int $signalingServer): b } /** - * @return bool True when the change was valid, false otherwise - * @throws \LengthException when the given description is too long + * @throws DescriptionException */ - public function setDescription(Room $room, string $description): bool { + public function setDescription(Room $room, string $description): void { $description = trim($description); + $oldDescription = $room->getDescription(); + if ($description === $oldDescription) { + return; + } + if (mb_strlen($description) > Room::DESCRIPTION_MAXIMUM_LENGTH) { - throw new \LengthException('Conversation description is limited to ' . Room::DESCRIPTION_MAXIMUM_LENGTH . ' characters'); + throw new DescriptionException(DescriptionException::REASON_VALUE); } - $oldDescription = $room->getDescription(); - if ($description === $oldDescription) { - return false; + if ($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) { + throw new DescriptionException(DescriptionException::REASON_TYPE); } $event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_DESCRIPTION, $description, $oldDescription); @@ -706,8 +710,6 @@ public function setDescription(Room $room, string $description): bool { $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_DESCRIPTION, $description, $oldDescription); $this->dispatcher->dispatchTyped($event); - - return true; } /** @@ -1032,14 +1034,10 @@ public function syncPropertiesFromHostRoom(Room $local, array $host): void { } if (isset($host['description']) && $host['description'] !== $local->getDescription()) { try { - $success = $this->setDescription($local, $host['description']); - if (!$success) { - $this->logger->error('An error occurred while trying to sync description of ' . $local->getId() . ' to ' . $host['description']); - } else { - $changed[] = ARoomModifiedEvent::PROPERTY_DESCRIPTION; - } - } catch (\LengthException $e) { - $this->logger->error('A \LengthException occurred while trying to sync description of ' . $local->getId() . ' to ' . $host['description'], ['exception' => $e]); + $this->setDescription($local, $host['description']); + $changed[] = ARoomModifiedEvent::PROPERTY_DESCRIPTION; + } catch (DescriptionException $e) { + $this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync description of ' . $local->getId() . ' to ' . $host['description'], ['exception' => $e]); } } if (isset($host['callRecording']) && $host['callRecording'] !== $local->getCallRecording()) { From 0206acd9bece2c5748ec50a6d813b6a93ddc9f35 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 10:38:16 +0200 Subject: [PATCH 07/11] fix(api): Properly typed password update Signed-off-by: Joas Schilling --- lib/Command/Room/TRoomCommand.php | 11 +++--- lib/Controller/RoomController.php | 21 +++++------ .../RoomProperty/PasswordException.php | 36 +++++++++++++++++++ lib/Service/RoomService.php | 18 +++++----- .../conversation-1/breakout-rooms.feature | 4 +-- 5 files changed, 62 insertions(+), 28 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/PasswordException.php diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php index c7054ceb98b..610d333bf69 100644 --- a/lib/Command/Room/TRoomCommand.php +++ b/lib/Command/Room/TRoomCommand.php @@ -15,6 +15,7 @@ use OCA\Talk\Exceptions\RoomProperty\DescriptionException; use OCA\Talk\Exceptions\RoomProperty\ListableException; 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; @@ -24,7 +25,6 @@ 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; @@ -164,11 +164,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.'); } } diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index f0b1ac7aef8..947c61b8b1d 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -23,6 +23,7 @@ use OCA\Talk\Exceptions\RoomProperty\LobbyException; use OCA\Talk\Exceptions\RoomProperty\MentionPermissionsException; 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; @@ -68,7 +69,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; @@ -1521,27 +1521,22 @@ public function setMentionPermissions(int $mentionPermissions): DataResponse { * Set a password for a room * * @param string $password New password - * @return DataResponse, array{}>|DataResponse + * @return DataResponse, array{}>|DataResponse * * 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(); diff --git a/lib/Exceptions/RoomProperty/PasswordException.php b/lib/Exceptions/RoomProperty/PasswordException.php new file mode 100644 index 00000000000..166281ecdc2 --- /dev/null +++ b/lib/Exceptions/RoomProperty/PasswordException.php @@ -0,0 +1,36 @@ +reason; + } + + public function getHint(): string { + return $this->hint; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 31c38ed90e6..1d029d3e557 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -34,6 +34,7 @@ use OCA\Talk\Exceptions\RoomProperty\LobbyException; use OCA\Talk\Exceptions\RoomProperty\MentionPermissionsException; 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; @@ -714,21 +715,24 @@ public function setDescription(Room $room, string $description): void { /** * @param string $password Currently it is only allowed to have a password for Room::TYPE_PUBLIC - * @return bool True when the change was valid, false otherwise - * @throws HintException + * @throws PasswordException */ - public function setPassword(Room $room, string $password): bool { + public function setPassword(Room $room, string $password): void { if ($room->getType() !== Room::TYPE_PUBLIC) { - return false; + throw new PasswordException(PasswordException::REASON_TYPE); } if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE) { - return false; + throw new PasswordException(PasswordException::REASON_BREAKOUT_ROOM); } if ($password !== '') { $event = new ValidatePasswordPolicyEvent($password); - $this->dispatcher->dispatchTyped($event); + try { + $this->dispatcher->dispatchTyped($event); + } catch (HintException $e) { + throw new PasswordException(PasswordException::REASON_VALUE, $e->getHint()); + } } $hash = $password !== '' ? $this->hasher->hash($password) : ''; @@ -746,8 +750,6 @@ public function setPassword(Room $room, string $password): bool { $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_PASSWORD, $password); $this->dispatcher->dispatchTyped($event); - - return true; } /** diff --git a/tests/integration/features/conversation-1/breakout-rooms.feature b/tests/integration/features/conversation-1/breakout-rooms.feature index 05a7aa4d786..a163a35ea76 100644 --- a/tests/integration/features/conversation-1/breakout-rooms.feature +++ b/tests/integration/features/conversation-1/breakout-rooms.feature @@ -908,8 +908,8 @@ Feature: conversation/breakout-rooms And user "participant1" allows listing room "Room 1" for "all" with 400 (v4) # Can not allow guests And user "participant1" makes room "Room 1" public with 400 (v4) - # Can not set password - Currently 403 because it's not a public room, once they are supported as breakout rooms we need to check for 400 here. - And user "participant1" sets password "Test123!" for room "Room 1" with 403 (v4) + # Can not set password + And user "participant1" sets password "Test123!" for room "Room 1" with 400 (v4) # Can not set message expiration And user "participant1" set the message expiration to 3600 of room "Room 1" with 400 (v4) # Can enable recording consent From 428aa9164a03c2ac1c5c6dffc17013db86bea7f4 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 10:43:12 +0200 Subject: [PATCH 08/11] fix(api): Properly typed messageExpiration update Signed-off-by: Joas Schilling --- lib/Command/Room/TRoomCommand.php | 7 ++++- lib/Controller/RoomController.php | 11 +++---- .../MessageExpirationException.php | 31 +++++++++++++++++++ lib/Service/RoomService.php | 19 +++++++++--- 4 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/MessageExpirationException.php diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php index 610d333bf69..dbde5ac73a3 100644 --- a/lib/Command/Room/TRoomCommand.php +++ b/lib/Command/Room/TRoomCommand.php @@ -14,6 +14,7 @@ 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; @@ -407,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.'); + } } } diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 947c61b8b1d..bbb38774574 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -22,6 +22,7 @@ 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; @@ -2353,7 +2354,7 @@ public function resendInvitations(?int $attendeeId): DataResponse { * * @param int $seconds New time * @psalm-param non-negative-int $seconds - * @return DataResponse, array{}>|DataResponse + * @return DataResponse, array{}>|DataResponse * * 200: Message expiration time updated successfully * 400: Updating message expiration time is not possible @@ -2361,14 +2362,10 @@ public function resendInvitations(?int $attendeeId): DataResponse { #[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(); diff --git a/lib/Exceptions/RoomProperty/MessageExpirationException.php b/lib/Exceptions/RoomProperty/MessageExpirationException.php new file mode 100644 index 00000000000..c172fb5d4e3 --- /dev/null +++ b/lib/Exceptions/RoomProperty/MessageExpirationException.php @@ -0,0 +1,31 @@ +reason; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 1d029d3e557..b709ffa0865 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -33,6 +33,7 @@ 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; @@ -773,11 +774,19 @@ public function verifyPassword(Room $room, string $password): array { } /** - * @throws InvalidArgumentException When the room is a breakout room or the room is a former one-to-one conversation + * @throws MessageExpirationException When the room is a breakout room or the room is a former one-to-one conversation */ public function setMessageExpiration(Room $room, int $seconds): void { - if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) { - throw new InvalidArgumentException('room'); + if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE) { + throw new MessageExpirationException(MessageExpirationException::REASON_BREAKOUT_ROOM); + } + + if ($room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) { + throw new MessageExpirationException(MessageExpirationException::REASON_TYPE); + } + + if ($seconds < 0) { + throw new MessageExpirationException(MessageExpirationException::REASON_VALUE); } $oldExpiration = $room->getMessageExpiration(); @@ -1110,8 +1119,8 @@ public function syncPropertiesFromHostRoom(Room $local, array $host): void { try { $this->setMessageExpiration($local, $host['messageExpiration']); $changed[] = ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION; - } catch (\InvalidArgumentException $e) { - $this->logger->error('An error (' . $e->getMessage() . ') occurred while trying to sync messageExpiration of ' . $local->getId() . ' to ' . $host['messageExpiration'], ['exception' => $e]); + } catch (MessageExpirationException $e) { + $this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync messageExpiration of ' . $local->getId() . ' to ' . $host['messageExpiration'], ['exception' => $e]); } } if (isset($host['readOnly']) && $host['readOnly'] !== $local->getReadOnly()) { From 38a244b86bcb4e7158524df7c63b35b4bb9d329b Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 10:50:40 +0200 Subject: [PATCH 09/11] fix(api): Properly typed breakoutRoom update Signed-off-by: Joas Schilling --- .../BreakoutRoomModeException.php | 29 +++++++++++++++++++ .../BreakoutRoomStatusException.php | 29 +++++++++++++++++++ lib/Service/BreakoutRoomService.php | 5 +++- lib/Service/RoomService.php | 22 +++++++++----- 4 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/BreakoutRoomModeException.php create mode 100644 lib/Exceptions/RoomProperty/BreakoutRoomStatusException.php diff --git a/lib/Exceptions/RoomProperty/BreakoutRoomModeException.php b/lib/Exceptions/RoomProperty/BreakoutRoomModeException.php new file mode 100644 index 00000000000..bdec69b53ed --- /dev/null +++ b/lib/Exceptions/RoomProperty/BreakoutRoomModeException.php @@ -0,0 +1,29 @@ +reason; + } +} diff --git a/lib/Exceptions/RoomProperty/BreakoutRoomStatusException.php b/lib/Exceptions/RoomProperty/BreakoutRoomStatusException.php new file mode 100644 index 00000000000..4bc139a3b62 --- /dev/null +++ b/lib/Exceptions/RoomProperty/BreakoutRoomStatusException.php @@ -0,0 +1,29 @@ +reason; + } +} diff --git a/lib/Service/BreakoutRoomService.php b/lib/Service/BreakoutRoomService.php index 5cc0b38c062..832905499fe 100644 --- a/lib/Service/BreakoutRoomService.php +++ b/lib/Service/BreakoutRoomService.php @@ -13,6 +13,7 @@ use OCA\Talk\Config; use OCA\Talk\Events\AAttendeeRemovedEvent; use OCA\Talk\Exceptions\ParticipantNotFoundException; +use OCA\Talk\Exceptions\RoomProperty\BreakoutRoomModeException; use OCA\Talk\Manager; use OCA\Talk\Model\Attendee; use OCA\Talk\Model\BreakoutRoom; @@ -115,7 +116,9 @@ public function setupBreakoutRooms(Room $parent, int $mode, int $amount, string throw new InvalidArgumentException('room'); } - if (!$this->roomService->setBreakoutRoomMode($parent, $mode)) { + try { + $this->roomService->setBreakoutRoomMode($parent, $mode); + } catch (BreakoutRoomModeException) { throw new InvalidArgumentException('mode'); } diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index b709ffa0865..6b7c8ea632d 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -27,6 +27,8 @@ use OCA\Talk\Events\RoomPasswordVerifyEvent; use OCA\Talk\Events\RoomSyncedEvent; use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Exceptions\RoomProperty\BreakoutRoomModeException; +use OCA\Talk\Exceptions\RoomProperty\BreakoutRoomStatusException; use OCA\Talk\Exceptions\RoomProperty\CallRecordingException; use OCA\Talk\Exceptions\RoomProperty\DefaultPermissionsException; use OCA\Talk\Exceptions\RoomProperty\DescriptionException; @@ -805,14 +807,18 @@ public function setMessageExpiration(Room $room, int $seconds): void { $this->dispatcher->dispatchTyped($event); } - public function setBreakoutRoomMode(Room $room, int $mode): bool { + /** + * @psalm-param BreakoutRoom::MODE_* $mode + * @throws BreakoutRoomModeException + */ + public function setBreakoutRoomMode(Room $room, int $mode): void { if (!in_array($mode, [ BreakoutRoom::MODE_NOT_CONFIGURED, BreakoutRoom::MODE_AUTOMATIC, BreakoutRoom::MODE_MANUAL, BreakoutRoom::MODE_FREE ], true)) { - return false; + throw new BreakoutRoomModeException(BreakoutRoomModeException::REASON_VALUE); } $oldMode = $room->getBreakoutRoomMode(); @@ -829,18 +835,20 @@ public function setBreakoutRoomMode(Room $room, int $mode): bool { $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_BREAKOUT_ROOM_MODE, $mode, $oldMode); $this->dispatcher->dispatchTyped($event); - - return true; } - public function setBreakoutRoomStatus(Room $room, int $status): bool { + /** + * @psalm-param BreakoutRoom::STATUS_* $status + * @throws BreakoutRoomStatusException + */ + public function setBreakoutRoomStatus(Room $room, int $status): void { if (!in_array($status, [ BreakoutRoom::STATUS_STOPPED, BreakoutRoom::STATUS_STARTED, BreakoutRoom::STATUS_ASSISTANCE_RESET, BreakoutRoom::STATUS_ASSISTANCE_REQUESTED, ], true)) { - return false; + throw new BreakoutRoomStatusException(BreakoutRoomStatusException::REASON_VALUE); } $oldStatus = $room->getBreakoutRoomStatus(); @@ -858,8 +866,6 @@ public function setBreakoutRoomStatus(Room $room, int $status): bool { $oldStatus = $room->getBreakoutRoomStatus(); $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_BREAKOUT_ROOM_STATUS, $status, $oldStatus); $this->dispatcher->dispatchTyped($event); - - return true; } /** From b13a01a11f6215169310bfd6fbcccee7ad746afc Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 11:11:47 +0200 Subject: [PATCH 10/11] fix(api): Properly typed avatar update Signed-off-by: Joas Schilling --- .../RoomProperty/AvatarException.php | 29 +++++++++++++++++++ lib/Service/RoomService.php | 17 ++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 lib/Exceptions/RoomProperty/AvatarException.php diff --git a/lib/Exceptions/RoomProperty/AvatarException.php b/lib/Exceptions/RoomProperty/AvatarException.php new file mode 100644 index 00000000000..907627bc152 --- /dev/null +++ b/lib/Exceptions/RoomProperty/AvatarException.php @@ -0,0 +1,29 @@ +reason; + } +} diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php index 6b7c8ea632d..a813e906596 100644 --- a/lib/Service/RoomService.php +++ b/lib/Service/RoomService.php @@ -27,6 +27,7 @@ use OCA\Talk\Events\RoomPasswordVerifyEvent; use OCA\Talk\Events\RoomSyncedEvent; use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Exceptions\RoomProperty\AvatarException; use OCA\Talk\Exceptions\RoomProperty\BreakoutRoomModeException; use OCA\Talk\Exceptions\RoomProperty\BreakoutRoomStatusException; use OCA\Talk\Exceptions\RoomProperty\CallRecordingException; @@ -420,9 +421,12 @@ public function setLobby(Room $room, int $newState, ?\DateTime $dateTime, bool $ } } - public function setAvatar(Room $room, string $avatar): bool { + /** + * @throws AvatarException + */ + public function setAvatar(Room $room, string $avatar): void { if ($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) { - return false; + throw new AvatarException(AvatarException::REASON_TYPE); } $oldAvatar = $room->getAvatar(); @@ -439,7 +443,6 @@ public function setAvatar(Room $room, string $avatar): bool { $event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_AVATAR, $avatar, $oldAvatar); $this->dispatcher->dispatchTyped($event); - return true; } /** @@ -1079,11 +1082,11 @@ public function syncPropertiesFromHostRoom(Room $local, array $host): void { // Add a fake suffix as we explode by the dot in the AvatarService, but the version doesn't have one. $hostAvatar .= '.fed'; } - $success = $this->setAvatar($local, $hostAvatar); - if (!$success) { - $this->logger->error('An error occurred while trying to sync avatarVersion of ' . $local->getId() . ' to ' . $host['avatarVersion']); - } else { + try { + $this->setAvatar($local, $hostAvatar); $changed[] = ARoomModifiedEvent::PROPERTY_AVATAR; + } catch (AvatarException $e) { + $this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync avatarVersion of ' . $local->getId() . ' to ' . $host['avatarVersion'], ['exception' => $e]); } } if (isset($host['lastActivity']) && $host['lastActivity'] !== 0 && $host['lastActivity'] !== ((int)$local->getLastActivity()?->getTimestamp())) { From f5e394fdcd16e4b2ccc95c561cfe6d89bd8f3516 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 29 Aug 2024 11:12:36 +0200 Subject: [PATCH 11/11] chore(assets): Recompile assets Signed-off-by: Joas Schilling --- openapi-full.json | 149 +++++++++++++++++++++++------- openapi.json | 149 +++++++++++++++++++++++------- src/types/openapi/openapi-full.ts | 49 +++++----- src/types/openapi/openapi.ts | 49 +++++----- 4 files changed, 284 insertions(+), 112 deletions(-) diff --git a/openapi-full.json b/openapi-full.json index 99d6e2907e6..57cd23cf210 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -11394,7 +11394,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } @@ -11500,7 +11515,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } @@ -11628,7 +11658,21 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "type", + "value" + ] + } + } + } } } } @@ -11760,7 +11804,21 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "type", + "value" + ] + } + } + } } } } @@ -11893,7 +11951,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } @@ -12001,34 +12074,6 @@ } } }, - "403": { - "description": "Setting password is not allowed", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } - } - } - } - }, "400": { "description": "Setting password is not possible", "content": { @@ -12051,7 +12096,18 @@ }, "data": { "type": "object", + "required": [ + "error" + ], "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + }, "message": { "type": "string" } @@ -15473,9 +15529,17 @@ }, "data": { "type": "object", + "required": [ + "error" + ], "properties": { "error": { - "type": "string" + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] } } } @@ -15716,7 +15780,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } diff --git a/openapi.json b/openapi.json index 24c01677c95..3e3a6b48a4f 100644 --- a/openapi.json +++ b/openapi.json @@ -11528,7 +11528,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } @@ -11634,7 +11649,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } @@ -11762,7 +11792,21 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "type", + "value" + ] + } + } + } } } } @@ -11894,7 +11938,21 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "type", + "value" + ] + } + } + } } } } @@ -12027,7 +12085,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } @@ -12135,34 +12208,6 @@ } } }, - "403": { - "description": "Setting password is not allowed", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } - } - } - } - }, "400": { "description": "Setting password is not possible", "content": { @@ -12185,7 +12230,18 @@ }, "data": { "type": "object", + "required": [ + "error" + ], "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + }, "message": { "type": "string" } @@ -15607,9 +15663,17 @@ }, "data": { "type": "object", + "required": [ + "error" + ], "properties": { "error": { - "type": "string" + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] } } } @@ -15850,7 +15914,22 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "breakout-room", + "type", + "value" + ] + } + } + } } } } diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index 2bc804d3006..dd7cc552370 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -6180,7 +6180,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; }; @@ -6225,7 +6228,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; }; @@ -6277,7 +6283,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "type" | "value"; + }; }; }; }; @@ -6333,7 +6342,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "type" | "value"; + }; }; }; }; @@ -6389,7 +6401,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; }; @@ -6442,26 +6457,14 @@ export interface operations { ocs: { meta: components["schemas"]["OCSMeta"]; data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; message?: string; }; }; }; }; }; - /** @description Setting password is not allowed */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: unknown; - }; - }; - }; - }; }; }; "room-set-permissions": { @@ -7890,7 +7893,8 @@ export interface operations { ocs: { meta: components["schemas"]["OCSMeta"]; data: { - error?: string; + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; }; }; }; @@ -7980,7 +7984,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; }; diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index f8c06b84173..d2ec65d2d52 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -5761,7 +5761,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; }; @@ -5806,7 +5809,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; }; @@ -5858,7 +5864,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "type" | "value"; + }; }; }; }; @@ -5914,7 +5923,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "type" | "value"; + }; }; }; }; @@ -5970,7 +5982,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; }; @@ -6023,26 +6038,14 @@ export interface operations { ocs: { meta: components["schemas"]["OCSMeta"]; data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; message?: string; }; }; }; }; }; - /** @description Setting password is not allowed */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: unknown; - }; - }; - }; - }; }; }; "room-set-permissions": { @@ -7471,7 +7474,8 @@ export interface operations { ocs: { meta: components["schemas"]["OCSMeta"]; data: { - error?: string; + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; }; }; }; @@ -7561,7 +7565,10 @@ export interface operations { "application/json": { ocs: { meta: components["schemas"]["OCSMeta"]; - data: unknown; + data: { + /** @enum {string} */ + error: "breakout-room" | "type" | "value"; + }; }; }; };