diff --git a/lib/Service/RowService.php b/lib/Service/RowService.php index 3e98e7d4a2..31461cd47f 100644 --- a/lib/Service/RowService.php +++ b/lib/Service/RowService.php @@ -635,9 +635,9 @@ public function updateSet( // is row in view? if (!$this->row2Mapper->isRowInViewPresent($id, $view, $userId)) { - $e = new \Exception('Update row is not allowed.'); + $e = new \Exception('Row not found.'); $this->logger->error($e->getMessage(), ['exception' => $e]); - throw new InternalError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); + throw new NotFoundError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); } // fetch all needed columns @@ -652,7 +652,7 @@ public function updateSet( if ($tableId !== $item->getTableId()) { $e = new \Exception('Row does not belong to table with id ' . $tableId); $this->logger->error($e->getMessage(), ['exception' => $e]); - throw new InternalError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); + throw new NotFoundError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); } // security @@ -755,20 +755,23 @@ public function delete(int $id, ?int $viewId, string $userId, ?int $tableId = nu } try { $view = $this->viewMapper->find($viewId); - } catch (InternalError|DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { + } catch (InternalError|MultipleObjectsReturnedException|Exception $e) { $this->logger->error($e->getMessage(), ['exception' => $e]); throw new InternalError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); + } catch (DoesNotExistException $e) { + $this->logger->error($e->getMessage(), ['exception' => $e]); + throw new NotFoundError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); } if (!$this->row2Mapper->isRowInViewPresent($id, $view, $userId)) { - $e = new \Exception('Update row is not allowed.'); + $e = new \Exception('Row not found.'); $this->logger->error($e->getMessage(), ['exception' => $e]); - throw new InternalError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); + throw new NotFoundError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); } } else { if ($tableId !== null && $tableId !== $item->getTableId()) { $e = new \Exception('Row does not belong to table with id ' . $tableId); $this->logger->error($e->getMessage(), ['exception' => $e]); - throw new InternalError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); + throw new NotFoundError(static::class . ' - ' . __FUNCTION__ . ': ' . $e->getMessage()); } // security diff --git a/tests/unit/Service/RowServiceRowNotFoundTest.php b/tests/unit/Service/RowServiceRowNotFoundTest.php new file mode 100644 index 0000000000..5ffc3e27aa --- /dev/null +++ b/tests/unit/Service/RowServiceRowNotFoundTest.php @@ -0,0 +1,131 @@ +setId(self::ROW_ID); + $row->setTableId(self::TABLE_ID); + + $this->row2Mapper = $this->createMock(Row2Mapper::class); + $this->row2Mapper->method('getTableIdForRow')->willReturn(self::TABLE_ID); + $this->row2Mapper->method('find')->willReturn($row); + $this->row2Mapper->expects($this->never())->method('update'); + $this->row2Mapper->expects($this->never())->method('delete'); + + $this->permissionsService = $this->createMock(PermissionsService::class); + $this->permissionsService->method('canReadRowsByElementId')->willReturn(true); + $this->permissionsService->method('canUpdateRowsByViewId')->willReturn(true); + $this->permissionsService->method('canUpdateRowsByTableId')->willReturn(true); + $this->permissionsService->method('canDeleteRowsByTableId')->willReturn(true); + + $this->viewMapper = $this->createMock(ViewMapper::class); + + $this->service = new RowService( + $this->createMock(LoggerInterface::class), + 'alice', + $this->permissionsService, + $this->createMock(ColumnMapper::class), + $this->viewMapper, + $this->createMock(TableMapper::class), + $this->row2Mapper, + $this->createMock(IEventDispatcher::class), + $this->createMock(ColumnsHelper::class), + $this->createMock(ActivityManager::class), + $this->createMock(NotificationHelper::class), + $this->createMock(IDBConnection::class), + ); + } + + private function givenTheViewExists(): void { + $view = new View(); + $view->setId(self::VIEW_ID); + $view->setTableId(self::TABLE_ID); + $this->viewMapper->method('find')->with(self::VIEW_ID)->willReturn($view); + } + + public function testUpdatingARowThatIsNotInTheViewIsNotFound(): void { + $this->givenTheViewExists(); + $this->row2Mapper->method('isRowInViewPresent')->willReturn(false); + + $this->expectException(NotFoundError::class); + $this->service->updateSet(self::ROW_ID, self::VIEW_ID, [], 'alice', null); + } + + public function testDeletingARowThatIsNotInTheViewIsNotFound(): void { + $this->givenTheViewExists(); + $this->permissionsService->method('canDeleteRowsByViewId')->willReturn(true); + $this->row2Mapper->method('isRowInViewPresent')->willReturn(false); + + $this->expectException(NotFoundError::class); + $this->service->delete(self::ROW_ID, self::VIEW_ID, 'alice'); + } + + public function testDeletingARowThroughAViewThatDoesNotExistIsNotFound(): void { + $this->permissionsService->method('canDeleteRowsByViewId')->willReturn(true); + $this->viewMapper->method('find')->willThrowException(new DoesNotExistException('View not found')); + + $this->expectException(NotFoundError::class); + $this->service->delete(self::ROW_ID, self::VIEW_ID, 'alice'); + } + + public function testUpdatingARowThatIsNotInTheTableIsNotFound(): void { + $this->expectException(NotFoundError::class); + $this->service->updateSet(self::ROW_ID, null, [], 'alice', self::TABLE_ID + 1); + } + + public function testDeletingARowThatIsNotInTheTableIsNotFound(): void { + $this->expectException(NotFoundError::class); + $this->service->delete(self::ROW_ID, null, 'alice', self::TABLE_ID + 1); + } + + public function testDeletingARowThroughAViewWithoutDeletePermissionIsForbidden(): void { + $this->permissionsService->method('canDeleteRowsByViewId')->willReturn(false); + $this->row2Mapper->expects($this->never())->method('isRowInViewPresent'); + + $this->expectException(PermissionError::class); + $this->service->delete(self::ROW_ID, self::VIEW_ID, 'alice'); + } +}