count events on remote calendars

This commit is contained in:
Julien Fastré 2022-07-01 23:28:42 +02:00
parent a604902074
commit 26a0ba4756
4 changed files with 91 additions and 9 deletions

View File

@ -69,11 +69,30 @@ class RemoteCalendarProxyController
throw new BadRequestHttpException('dateTo not parsable'); throw new BadRequestHttpException('dateTo not parsable');
} }
$events = $this->remoteCalendarConnector->listEventsForUser($user, $dateFrom, $dateTo); $total = $this->remoteCalendarConnector->countEventsForUser($user, $dateFrom, $dateTo);
$paginator = $this->paginatorFactory->create(count($events)); $paginator = $this->paginatorFactory->create($total);
if (count($events) > 0) { if (0 === $total) {
$paginator->setItemsPerPage($paginator->getTotalItems()); 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); $collection = new Collection($events, $paginator);

View File

@ -29,10 +29,13 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
use function count; use function count;
class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
{ {
private array $cacheScheduleTimeForUser = [];
private CalendarRangeRepository $calendarRangeRepository; private CalendarRangeRepository $calendarRangeRepository;
private LoggerInterface $logger; private LoggerInterface $logger;
@ -73,6 +76,43 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$this->userHttpClient = $userHttpClient; $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 public function getMakeReadyResponse(string $returnPath): Response
{ {
return new RedirectResponse($this->urlGenerator return new RedirectResponse($this->urlGenerator
@ -84,7 +124,15 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
return $this->tokenStorage->hasToken(); 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); $userId = $this->mapCalendarToUser->getUserId($user);
@ -101,6 +149,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
'startDateTime' => $startDate->format(DateTimeImmutable::ATOM), 'startDateTime' => $startDate->format(DateTimeImmutable::ATOM),
'endDateTime' => $endDate->format(DateTimeImmutable::ATOM), 'endDateTime' => $endDate->format(DateTimeImmutable::ATOM),
'$select' => 'id,subject,start,end,isAllDay', '$select' => 'id,subject,start,end,isAllDay',
'$top' => $limit,
'$skip' => $offset,
], ],
] ]
)->toArray(); )->toArray();
@ -127,7 +177,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
return $this->getScheduleTimesForUser($user, $startDate, $endDate); 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_code' => $e->getResponse()->getStatusCode(),
'error' => $e->getResponse()->getInfo(), 'error' => $e->getResponse()->getInfo(),
]); ]);
@ -475,6 +525,10 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
{ {
$userId = $this->mapCalendarToUser->getUserId($user); $userId = $this->mapCalendarToUser->getUserId($user);
if (array_key_exists($userId, $this->cacheScheduleTimeForUser)) {
return $this->cacheScheduleTimeForUser[$userId];
}
if (null === $userId) { if (null === $userId) {
return []; return [];
} }
@ -508,12 +562,14 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
return []; return [];
} }
return array_map( $this->cacheScheduleTimeForUser[$userId] = array_map(
function ($item) { function ($item) {
return $this->remoteEventConverter->convertAvailabilityToRemoteEvent($item); return $this->remoteEventConverter->convertAvailabilityToRemoteEvent($item);
}, },
$response['value'][0]['scheduleItems'] $response['value'][0]['scheduleItems']
); );
return $this->cacheScheduleTimeForUser[$userId];
} }
private function patchCalendarOnRemote(Calendar $calendar, array $newInvites): void private function patchCalendarOnRemote(Calendar $calendar, array $newInvites): void

View File

@ -21,6 +21,11 @@ use Symfony\Component\HttpFoundation\Response;
class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface
{ {
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int
{
return 0;
}
public function getMakeReadyResponse(string $returnPath): Response public function getMakeReadyResponse(string $returnPath): Response
{ {
throw new LogicException('As this connector is always ready, this method should not be called'); 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; 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 []; return [];
} }

View File

@ -21,6 +21,8 @@ use Symfony\Component\HttpFoundation\Response;
interface RemoteCalendarConnectorInterface interface RemoteCalendarConnectorInterface
{ {
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int;
/** /**
* Return a response, more probably a RedirectResponse, where the user * Return a response, more probably a RedirectResponse, where the user
* will be able to fullfill requirements to prepare this connector and * will be able to fullfill requirements to prepare this connector and
@ -37,7 +39,7 @@ interface RemoteCalendarConnectorInterface
/** /**
* @return array|RemoteEvent[] * @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; public function removeCalendar(string $remoteId, array $remoteAttributes, User $user, ?CalendarRange $associatedCalendarRange = null): void;