From eb253e24f756acd960588b2e450541354d8cfbf5 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Thu, 18 Jul 2024 17:55:59 +0200 Subject: [PATCH] Check hard limit in paginated responses --- lib/Client/List/PaginatedFilesList.php | 3 +- lib/Client/List/PaginatedGameList.php | 4 +- lib/Client/List/PaginatedList.php | 52 ++++++++++++++++++------- lib/Client/List/PaginatedModList.php | 3 +- tests/Integration/Client/ClientTest.php | 21 ++++++++++ 5 files changed, 66 insertions(+), 17 deletions(-) diff --git a/lib/Client/List/PaginatedFilesList.php b/lib/Client/List/PaginatedFilesList.php index bba8911..3b9444a 100644 --- a/lib/Client/List/PaginatedFilesList.php +++ b/lib/Client/List/PaginatedFilesList.php @@ -28,10 +28,11 @@ public function __construct( }, $filesResponse->getData())); } - public function getOffset(int $offset): static + public function getOffset(int $offset, int $pageSize): static { $options = clone $this->options; $options->setOffset($offset); + $options->setPageSize($pageSize); return $this->client->getModFiles($options); } } \ No newline at end of file diff --git a/lib/Client/List/PaginatedGameList.php b/lib/Client/List/PaginatedGameList.php index 02e931a..68dffeb 100644 --- a/lib/Client/List/PaginatedGameList.php +++ b/lib/Client/List/PaginatedGameList.php @@ -26,8 +26,8 @@ public function __construct( }, $gamesResponse->getData())); } - public function getOffset(int $offset): static + public function getOffset(int $offset, int $pageSize): static { - return $this->client->getGames($offset, $this->pagination->getPageSize()); + return $this->client->getGames($offset, $pageSize); } } \ No newline at end of file diff --git a/lib/Client/List/PaginatedList.php b/lib/Client/List/PaginatedList.php index 3f26877..55dc87b 100644 --- a/lib/Client/List/PaginatedList.php +++ b/lib/Client/List/PaginatedList.php @@ -18,6 +18,12 @@ */ abstract class PaginatedList implements Iterator, ArrayAccess, Countable { + /** + * The limit for how many results are allowed to be requested. + * @type int + */ + public const LIMIT = 10_000; + protected int $iterator = 0; protected function __construct( @@ -30,6 +36,15 @@ protected function __construct( { } + /** + * Get the pagination part of the response + * @return Pagination + */ + public function getPagination(): Pagination + { + return $this->pagination; + } + /** * @return T[] */ @@ -41,10 +56,11 @@ public function getResults(): array /** * Get a new page of results starting at the given offset * @param int $offset + * @param int $pageSize * @return $this * @throws ApiException */ - public abstract function getOffset(int $offset): static; + public abstract function getOffset(int $offset, int $pageSize): static; /** * returns true if there is a next page with results on it @@ -52,7 +68,7 @@ public abstract function getOffset(int $offset): static; */ public function hasNextPage(): bool { - return $this->pagination->getTotalCount() > $this->getNextOffset(); + return min($this->pagination->getTotalCount(), static::LIMIT) > $this->getNextOffset(); } /** @@ -67,16 +83,8 @@ public function getNextPage(): ?static return null; } - return $this->getOffset($this->getNextOffset()); - } - - /** - * get the offset of the next page - * @return int - */ - protected function getNextOffset(): int - { - return $this->pagination->getIndex() + $this->pagination->getPageSize(); + $offset = $this->getNextOffset(); + return $this->getOffset($offset, $this->getNextPageSize($offset)); } /** @@ -110,7 +118,7 @@ public function getPreviousPage(): ?static return null; } - return $this->getOffset($this->getPreviousOffset()); + return $this->getOffset($this->getPreviousOffset(), $this->pagination->getPageSize()); } /** @@ -220,4 +228,22 @@ public function count(): int { return count($this->results); } + + /** + * @param int $offset + * @return int + */ + protected function getNextPageSize(int $offset): int + { + return min($this->pagination->getPageSize(), static::LIMIT - $offset); + } + + /** + * get the offset of the next page + * @return int + */ + protected function getNextOffset(): int + { + return $this->pagination->getIndex() + $this->pagination->getPageSize(); + } } \ No newline at end of file diff --git a/lib/Client/List/PaginatedModList.php b/lib/Client/List/PaginatedModList.php index 7e4db7a..9b39689 100644 --- a/lib/Client/List/PaginatedModList.php +++ b/lib/Client/List/PaginatedModList.php @@ -28,10 +28,11 @@ public function __construct( }, $this->response->getData())); } - public function getOffset(int $offset): static + public function getOffset(int $offset, int $pageSize): static { $options = clone $this->options; $options->setOffset($offset); + $options->setPageSize($pageSize); return $this->client->searchMods($options); } } \ No newline at end of file diff --git a/tests/Integration/Client/ClientTest.php b/tests/Integration/Client/ClientTest.php index 82f683b..e9f5da3 100644 --- a/tests/Integration/Client/ClientTest.php +++ b/tests/Integration/Client/ClientTest.php @@ -210,6 +210,27 @@ public function testSearchMods() } } + public function testSearchModsLastPage() + { + $options = new ModSearchOptions(static::MINECRAFT_GAME_ID); + $options->setPageSize(50); + $options->setOffset(9_910); + + $mods = $this->apiClient->searchMods($options); + $this->assertNotEmpty($mods); + foreach ($mods as $mod) { + $this->assertEquals(static::MINECRAFT_GAME_ID, $mod->getData()->getGameId()); + } + + $this->assertTrue($mods->hasNextPage()); + $mods = $mods->getNextPage(); + + $this->assertNotEmpty($mods); + $this->assertFalse($mods->hasNextPage()); + $this->assertEquals($mods::LIMIT, $mods->getPagination()->getIndex() + $mods->getPagination()->getPageSize()); + } + + /** * Test searching mods in a category class * @return void