Compare commits

...

5 Commits

Author SHA1 Message Date
Kevin Papst
233c0e792e merge main 2024-09-22 20:26:03 +02:00
Kevin Papst
896e8b42c1 merge main 2024-09-22 20:11:37 +02:00
Kevin Papst
bf288bf115 Merge branch 'refs/heads/main' into api-pagination
# Conflicts:
#	src/API/BaseApiController.php
2024-09-22 17:34:24 +02:00
Kevin Papst
0f9b964b11 Merge remote-tracking branch 'refs/remotes/origin/api-enhancements' into api-pagination
# Conflicts:
#	src/API/ActivityController.php
#	src/API/CustomerController.php
#	src/API/ProjectController.php
#	src/API/TimesheetController.php
#	src/API/UserController.php
#	src/Repository/ProjectRepository.php
2024-09-22 16:34:24 +02:00
Kevin Papst
3e3cdc5e69 global pagination support for API collection calls 2024-03-10 13:27:04 +01:00
7 changed files with 39 additions and 44 deletions

View File

@@ -8,6 +8,18 @@ you can upgrade your Kimai installation to the latest stable release.
Check below if there are more version specific steps required, which need to be executed after the normal update process.
Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation.
## [2.14.0](https://github.com/kimai/kimai/releases/tag/2.13.0)
**DEVELOPERS ONLY**
The API now uses pagination globally for all collection endpoints. This might break current implementations
All collections are limited to 50 entries by default (changes in Customer, Project, Activity, User).
The parameters to influence pagination, page size and page number are `size` (and `pageSize`) and `page`.
Read the [API pagination docs](https://www.kimai.org/documentation/api-pagination.html) for more infos.
## [2.0.30](https://github.com/kimai/kimai/releases/tag/2.0.30)
The `DATABASE_URL` in your environment settings (e.g. [.env](https://github.com/kimai/kimai/issues/4246), [docker-compose.yaml](https://github.com/tobybatch/kimai2/issues/531) or webserver config)

View File

@@ -62,14 +62,13 @@ final class ActivityController extends BaseApiController
#[Rest\QueryParam(name: 'orderBy', requirements: 'id|name|project', strict: true, nullable: true, description: 'The field by which results will be ordered. Allowed values: id, name, project (default: name)')]
#[Rest\QueryParam(name: 'order', requirements: 'ASC|DESC', strict: true, nullable: true, description: 'The result order. Allowed values: ASC, DESC (default: ASC)')]
#[Rest\QueryParam(name: 'term', description: 'Free search term')]
#[Rest\QueryParam(name: 'page', requirements: '\d+', strict: true, nullable: true, description: 'The page to display, renders a 404 if not found (default: 1)')]
#[Rest\QueryParam(name: 'size', requirements: '\d+', strict: true, nullable: true, description: 'The amount of entries for each page (default: 50)')]
public function cgetAction(ParamFetcherInterface $paramFetcher, ProjectRepository $projectRepository): Response
{
/** @var User $user */
$user = $this->getUser();
$query = new ActivityQuery();
$query->loadTeams();
$query->setCurrentUser($user);
$this->prepareQuery($query, $paramFetcher);
$order = $paramFetcher->get('order');
if (\is_string($order) && $order !== '') {
@@ -110,9 +109,9 @@ final class ActivityController extends BaseApiController
$query->setSearchTerm(new SearchTerm($term));
}
$query->setIsApiCall(true);
$data = $this->repository->getActivitiesForQuery($query);
$view = new View($data, 200);
$pagination = $this->repository->getPagerfantaForQuery($query);
$view = $this->createPaginatedView($pagination);
$view->getContext()->setGroups(self::GROUPS_COLLECTION);
return $this->viewHandler->handle($view);

View File

@@ -59,14 +59,13 @@ final class CustomerController extends BaseApiController
#[Rest\QueryParam(name: 'order', requirements: 'ASC|DESC', strict: true, nullable: true, description: 'The result order. Allowed values: ASC, DESC (default: ASC)')]
#[Rest\QueryParam(name: 'orderBy', requirements: 'id|name', strict: true, nullable: true, description: 'The field by which results will be ordered. Allowed values: id, name (default: name)')]
#[Rest\QueryParam(name: 'term', description: 'Free search term')]
#[Rest\QueryParam(name: 'page', requirements: '\d+', strict: true, nullable: true, description: 'The page to display, renders a 404 if not found (default: 1)')]
#[Rest\QueryParam(name: 'size', requirements: '\d+', strict: true, nullable: true, description: 'The amount of entries for each page (default: 50)')]
public function cgetAction(ParamFetcherInterface $paramFetcher): Response
{
/** @var User $user */
$user = $this->getUser();
$query = new CustomerQuery();
$query->loadTeams();
$query->setCurrentUser($user);
$this->prepareQuery($query, $paramFetcher);
$order = $paramFetcher->get('order');
if (\is_string($order) && $order !== '') {
@@ -88,9 +87,9 @@ final class CustomerController extends BaseApiController
$query->setSearchTerm(new SearchTerm($term));
}
$query->setIsApiCall(true);
$data = $this->repository->getCustomersForQuery($query);
$view = new View($data, 200);
$pagination = $this->repository->getPagerfantaForQuery($query);
$view = $this->createPaginatedView($pagination);
$view->getContext()->setGroups(self::GROUPS_COLLECTION);
return $this->viewHandler->handle($view);

View File

@@ -68,14 +68,13 @@ final class ProjectController extends BaseApiController
#[Rest\QueryParam(name: 'order', requirements: 'ASC|DESC', strict: true, nullable: true, description: 'The result order. Allowed values: ASC, DESC (default: ASC)')]
#[Rest\QueryParam(name: 'orderBy', requirements: 'id|name|customer', strict: true, nullable: true, description: 'The field by which results will be ordered. Allowed values: id, name, customer (default: name)')]
#[Rest\QueryParam(name: 'term', description: 'Free search term')]
#[Rest\QueryParam(name: 'page', requirements: '\d+', strict: true, nullable: true, description: 'The page to display, renders a 404 if not found (default: 1)')]
#[Rest\QueryParam(name: 'size', requirements: '\d+', strict: true, nullable: true, description: 'The amount of entries for each page (default: 50)')]
public function cgetAction(ParamFetcherInterface $paramFetcher, CustomerRepository $customerRepository): Response
{
/** @var User $user */
$user = $this->getUser();
$query = new ProjectQuery();
$query->loadTeams();
$query->setCurrentUser($user);
$this->prepareQuery($query, $paramFetcher);
$order = $paramFetcher->get('order');
if (\is_string($order) && $order !== '') {
@@ -142,9 +141,9 @@ final class ProjectController extends BaseApiController
$query->setSearchTerm(new SearchTerm($term));
}
$query->setIsApiCall(true);
$data = $this->repository->getProjectsForQuery($query);
$view = new View($data, 200);
$pagination = $this->repository->getPagerfantaForQuery($query);
$view = $this->createPaginatedView($pagination);
$view->getContext()->setGroups(self::GROUPS_COLLECTION);
return $this->viewHandler->handle($view);

View File

@@ -96,7 +96,7 @@ final class TimesheetController extends BaseApiController
public function cgetAction(ParamFetcherInterface $paramFetcher, CustomerRepository $customerRepository, ProjectRepository $projectRepository, ActivityRepository $activityRepository, UserRepository $userRepository): Response
{
$query = new TimesheetQuery(false);
$query->setCurrentUser($this->getUser());
$this->prepareQuery($query, $paramFetcher);
$seeAll = false;
if ($this->isGranted('view_other_timesheet')) {
@@ -168,16 +168,6 @@ final class TimesheetController extends BaseApiController
$query->addActivity($activity);
}
$page = $paramFetcher->get('page');
if (\is_string($page) && $page !== '') {
$query->setPage((int) $page);
}
$size = $paramFetcher->get('size');
if (\is_string($size) && $size !== '') {
$query->setPageSize((int) $size);
}
/** @var array<string> $tags */
$tags = $paramFetcher->get('tags');
if (\is_array($tags) && \count($tags) > 0) {
@@ -248,12 +238,8 @@ final class TimesheetController extends BaseApiController
$query->setModifiedAfter($factory->createDateTime($modifiedAfter));
}
$query->setIsApiCall(true);
$data = $this->repository->getPagerfantaForQuery($query);
$results = (array) $data->getCurrentPageResults();
$view = new View($results, 200);
$this->addPagination($view, $data);
$pagination = $this->repository->getPagerfantaForQuery($query);
$view = $this->createPaginatedView($pagination);
$full = $paramFetcher->get('full');
if ($full === '1' || $full === 'true') {

View File

@@ -57,10 +57,12 @@ final class UserController extends BaseApiController
#[Rest\QueryParam(name: 'order', requirements: 'ASC|DESC', strict: true, nullable: true, description: 'The result order. Allowed values: ASC, DESC (default: ASC)')]
#[Rest\QueryParam(name: 'term', description: 'Free search term')]
#[Rest\QueryParam(name: 'full', requirements: '0|1|true|false', strict: true, nullable: true, description: 'Allows to fetch full objects including subresources. Allowed values: 0|1|false|true (default: false)')]
#[Rest\QueryParam(name: 'page', requirements: '\d+', strict: true, nullable: true, description: 'The page to display, renders a 404 if not found (default: 1)')]
#[Rest\QueryParam(name: 'size', requirements: '\d+', strict: true, nullable: true, description: 'The amount of entries for each page (default: 50)')]
public function cgetAction(ParamFetcherInterface $paramFetcher): Response
{
$query = new UserQuery();
$query->setCurrentUser($this->getUser());
$this->prepareQuery($query, $paramFetcher);
$visible = $paramFetcher->get('visible');
if (\is_string($visible) && $visible !== '') {
@@ -82,9 +84,8 @@ final class UserController extends BaseApiController
$query->setSearchTerm(new SearchTerm($term));
}
$query->setIsApiCall(true);
$data = $this->repository->getUsersForQuery($query);
$view = new View($data, 200);
$pagination = $this->repository->getPagerfantaForQuery($query);
$view = $this->createPaginatedView($pagination);
$full = $paramFetcher->get('full');
if ($full === '1' || $full === 'true') {

View File

@@ -41,9 +41,8 @@ class PaginationTest extends TestCase
$query = new TimesheetQuery();
$query->setPage(3);
$query->setPageSize(1);
$query->setIsApiCall(true);
$sut = new Pagination(new ArrayAdapter([1, 2, 3, 4, 5]), $query);
$this->assertFalse($sut->getNormalizeOutOfRangePages());
$this->assertTrue($sut->getNormalizeOutOfRangePages());
$this->assertEquals(1, $sut->getMaxPerPage());
$this->assertEquals(3, $sut->getCurrentPage());
}