From 26a0ba475686811364967026e7372a1b2b316bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 1 Jul 2022 23:28:42 +0200 Subject: [PATCH] count events on remote calendars --- .../RemoteCalendarProxyController.php | 27 ++++++-- .../MSGraphRemoteCalendarConnector.php | 62 ++++++++++++++++++- .../Connector/NullRemoteCalendarConnector.php | 7 ++- .../RemoteCalendarConnectorInterface.php | 4 +- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php b/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php index 4792660e0..0523bbd19 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php @@ -69,11 +69,30 @@ class RemoteCalendarProxyController throw new BadRequestHttpException('dateTo not parsable'); } - $events = $this->remoteCalendarConnector->listEventsForUser($user, $dateFrom, $dateTo); - $paginator = $this->paginatorFactory->create(count($events)); + $total = $this->remoteCalendarConnector->countEventsForUser($user, $dateFrom, $dateTo); + $paginator = $this->paginatorFactory->create($total); - if (count($events) > 0) { - $paginator->setItemsPerPage($paginator->getTotalItems()); + if (0 === $total) { + return new JsonResponse( + $this->serializer->serialize(new Collection([], $paginator), 'json'), + JsonResponse::HTTP_OK, + [], + true + ); + } + + $events = $this->remoteCalendarConnector->listEventsForUser( + $user, + $dateFrom, + $dateTo, + $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage() + ); + + // in some case, we cannot paginate: we have to fetch all the items at once. We must avoid + // further requests by forcing the number of items returned. + if (count($events) > $paginator->getItemsPerPage()) { + $paginator->setItemsPerPage(count($events)); } $collection = new Collection($events, $paginator); diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php index 9f83e6ebd..7f85273c8 100644 --- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php +++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php @@ -29,10 +29,13 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function array_key_exists; use function count; class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface { + private array $cacheScheduleTimeForUser = []; + private CalendarRangeRepository $calendarRangeRepository; private LoggerInterface $logger; @@ -73,6 +76,43 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface $this->userHttpClient = $userHttpClient; } + public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int + { + $userId = $this->mapCalendarToUser->getUserId($user); + + if (null === $userId) { + return 0; + } + + try { + $data = $this->userHttpClient->request( + 'GET', + 'users/' . $userId . '/calendarView', + [ + 'query' => [ + 'startDateTime' => $startDate->format(DateTimeImmutable::ATOM), + 'endDateTime' => $endDate->format(DateTimeImmutable::ATOM), + '$count' => 'true', + '$top' => 0, + ], + ] + )->toArray(); + } catch (ClientExceptionInterface $e) { + if (403 === $e->getResponse()->getStatusCode()) { + return count($this->getScheduleTimesForUser($user, $startDate, $endDate)); + } + + $this->logger->error('Could not get list of event on MSGraph', [ + 'error_code' => $e->getResponse()->getStatusCode(), + 'error' => $e->getResponse()->getInfo(), + ]); + + return 0; + } + + return $data['@odata.count']; + } + public function getMakeReadyResponse(string $returnPath): Response { return new RedirectResponse($this->urlGenerator @@ -84,7 +124,15 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface return $this->tokenStorage->hasToken(); } - public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array + /** + * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface + * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface + * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface + * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface + * + * @return array|\Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent[] + */ + public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array { $userId = $this->mapCalendarToUser->getUserId($user); @@ -101,6 +149,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface 'startDateTime' => $startDate->format(DateTimeImmutable::ATOM), 'endDateTime' => $endDate->format(DateTimeImmutable::ATOM), '$select' => 'id,subject,start,end,isAllDay', + '$top' => $limit, + '$skip' => $offset, ], ] )->toArray(); @@ -127,7 +177,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface return $this->getScheduleTimesForUser($user, $startDate, $endDate); } - $this->logger->debug('Could not get list of event on MSGraph', [ + $this->logger->error('Could not get list of event on MSGraph', [ 'error_code' => $e->getResponse()->getStatusCode(), 'error' => $e->getResponse()->getInfo(), ]); @@ -475,6 +525,10 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface { $userId = $this->mapCalendarToUser->getUserId($user); + if (array_key_exists($userId, $this->cacheScheduleTimeForUser)) { + return $this->cacheScheduleTimeForUser[$userId]; + } + if (null === $userId) { return []; } @@ -508,12 +562,14 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface return []; } - return array_map( + $this->cacheScheduleTimeForUser[$userId] = array_map( function ($item) { return $this->remoteEventConverter->convertAvailabilityToRemoteEvent($item); }, $response['value'][0]['scheduleItems'] ); + + return $this->cacheScheduleTimeForUser[$userId]; } private function patchCalendarOnRemote(Calendar $calendar, array $newInvites): void diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/NullRemoteCalendarConnector.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/NullRemoteCalendarConnector.php index 1f163e569..54440053b 100644 --- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/NullRemoteCalendarConnector.php +++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/NullRemoteCalendarConnector.php @@ -21,6 +21,11 @@ use Symfony\Component\HttpFoundation\Response; class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface { + public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int + { + return 0; + } + public function getMakeReadyResponse(string $returnPath): Response { throw new LogicException('As this connector is always ready, this method should not be called'); @@ -31,7 +36,7 @@ class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface return true; } - public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array + public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array { return []; } diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/RemoteCalendarConnectorInterface.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/RemoteCalendarConnectorInterface.php index 625607d40..c6ae4610d 100644 --- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/RemoteCalendarConnectorInterface.php +++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/RemoteCalendarConnectorInterface.php @@ -21,6 +21,8 @@ use Symfony\Component\HttpFoundation\Response; interface RemoteCalendarConnectorInterface { + public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int; + /** * Return a response, more probably a RedirectResponse, where the user * will be able to fullfill requirements to prepare this connector and @@ -37,7 +39,7 @@ interface RemoteCalendarConnectorInterface /** * @return array|RemoteEvent[] */ - public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array; + public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array; public function removeCalendar(string $remoteId, array $remoteAttributes, User $user, ?CalendarRange $associatedCalendarRange = null): void;