mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
761 lines
27 KiB
PHP
761 lines
27 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Chill is a software for social workers.
|
|
*
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* Chill is a software for social workers
|
|
*
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Chill\CalendarBundle\RemoteCalendar\Connector;
|
|
|
|
use Chill\CalendarBundle\Entity\Calendar;
|
|
use Chill\CalendarBundle\Entity\CalendarRange;
|
|
use Chill\CalendarBundle\Entity\Invite;
|
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MapCalendarToUser;
|
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\OnBehalfOfUserHttpClient;
|
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\OnBehalfOfUserTokenStorage;
|
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteEventConverter;
|
|
use Chill\CalendarBundle\Repository\CalendarRangeRepository;
|
|
use Chill\CalendarBundle\Repository\CalendarRepository;
|
|
use Chill\MainBundle\Entity\User;
|
|
use DateTimeImmutable;
|
|
use Exception;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
use Symfony\Component\Security\Core\Security;
|
|
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
use function array_key_exists;
|
|
use function count;
|
|
|
|
class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
|
{
|
|
private array $cacheScheduleTimeForUser = [];
|
|
|
|
private CalendarRangeRepository $calendarRangeRepository;
|
|
|
|
private CalendarRepository $calendarRepository;
|
|
|
|
private LoggerInterface $logger;
|
|
|
|
private HttpClientInterface $machineHttpClient;
|
|
|
|
private MapCalendarToUser $mapCalendarToUser;
|
|
|
|
private RemoteEventConverter $remoteEventConverter;
|
|
|
|
private OnBehalfOfUserTokenStorage $tokenStorage;
|
|
|
|
private TranslatorInterface $translator;
|
|
|
|
private UrlGeneratorInterface $urlGenerator;
|
|
|
|
private OnBehalfOfUserHttpClient $userHttpClient;
|
|
|
|
private Security $security;
|
|
|
|
public function __construct(
|
|
CalendarRepository $calendarRepository,
|
|
CalendarRangeRepository $calendarRangeRepository,
|
|
HttpClientInterface $machineHttpClient,
|
|
MapCalendarToUser $mapCalendarToUser,
|
|
LoggerInterface $logger,
|
|
OnBehalfOfUserTokenStorage $tokenStorage,
|
|
OnBehalfOfUserHttpClient $userHttpClient,
|
|
RemoteEventConverter $remoteEventConverter,
|
|
TranslatorInterface $translator,
|
|
UrlGeneratorInterface $urlGenerator,
|
|
Security $security
|
|
) {
|
|
$this->calendarRepository = $calendarRepository;
|
|
$this->calendarRangeRepository = $calendarRangeRepository;
|
|
$this->machineHttpClient = $machineHttpClient;
|
|
$this->mapCalendarToUser = $mapCalendarToUser;
|
|
$this->logger = $logger;
|
|
$this->remoteEventConverter = $remoteEventConverter;
|
|
$this->tokenStorage = $tokenStorage;
|
|
$this->translator = $translator;
|
|
$this->urlGenerator = $urlGenerator;
|
|
$this->userHttpClient = $userHttpClient;
|
|
$this->security = $security;
|
|
}
|
|
|
|
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->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
|
|
'endDateTime' => $endDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
|
|
'$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
|
|
->generate('chill_calendar_remote_connect_azure', ['returnPath' => $returnPath]));
|
|
}
|
|
|
|
public function isReady(): bool
|
|
{
|
|
$user = $this->security->getUser();
|
|
|
|
if (!$user instanceof User) {
|
|
// this is not a user from chill. This is not the role of this class to
|
|
// restrict access, so we will just say that we do not have to do anything more
|
|
// here...
|
|
return true;
|
|
}
|
|
|
|
if (null === $this->mapCalendarToUser->getUserId($user)) {
|
|
// this user is not mapped with remote calendar. The user will have to wait for
|
|
// the next calendar subscription iteration
|
|
$this->logger->debug('mark user ready for msgraph calendar as he does not have any mapping', [
|
|
'userId' => $user->getId(),
|
|
]);
|
|
return true;
|
|
}
|
|
|
|
return $this->tokenStorage->hasToken();
|
|
}
|
|
|
|
/**
|
|
* @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);
|
|
|
|
if (null === $userId) {
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
$bareEvents = $this->userHttpClient->request(
|
|
'GET',
|
|
'users/' . $userId . '/calendarView',
|
|
[
|
|
'query' => [
|
|
'startDateTime' => $startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
|
|
'endDateTime' => $endDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
|
|
'$select' => 'id,subject,start,end,isAllDay',
|
|
'$top' => $limit,
|
|
'$skip' => $offset,
|
|
],
|
|
]
|
|
)->toArray();
|
|
|
|
$ids = array_map(static fn ($item) => $item['id'], $bareEvents['value']);
|
|
$existingIdsInRange = $this->calendarRangeRepository->findRemoteIdsPresent($ids);
|
|
$existingIdsInCalendar = $this->calendarRepository->findRemoteIdsPresent($ids);
|
|
|
|
return array_values(
|
|
array_map(
|
|
fn ($item) => $this->remoteEventConverter->convertToRemote($item),
|
|
// filter all event to keep only the one not in range
|
|
array_filter(
|
|
$bareEvents['value'],
|
|
static fn ($item) => ((!$existingIdsInRange[$item['id']]) ?? true) && ((!$existingIdsInCalendar[$item['id']]) ?? true)
|
|
)
|
|
)
|
|
);
|
|
} catch (ClientExceptionInterface $e) {
|
|
if (403 === $e->getResponse()->getStatusCode()) {
|
|
return $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 [];
|
|
}
|
|
}
|
|
|
|
public function removeCalendar(string $remoteId, array $remoteAttributes, User $user, ?CalendarRange $associatedCalendarRange = null): void
|
|
{
|
|
if ('' === $remoteId) {
|
|
return;
|
|
}
|
|
|
|
$this->removeEvent($remoteId, $user);
|
|
|
|
if (null !== $associatedCalendarRange) {
|
|
$this->syncCalendarRange($associatedCalendarRange);
|
|
}
|
|
}
|
|
|
|
public function removeCalendarRange(string $remoteId, array $remoteAttributes, User $user): void
|
|
{
|
|
if ('' === $remoteId) {
|
|
return;
|
|
}
|
|
|
|
$this->removeEvent($remoteId, $user);
|
|
}
|
|
|
|
public function syncCalendar(Calendar $calendar, string $action, ?CalendarRange $previousCalendarRange, ?User $previousMainUser, ?array $oldInvites, ?array $newInvites): void
|
|
{
|
|
/*
|
|
* cases to support:
|
|
*
|
|
* * a calendar range is created:
|
|
* * create on remote
|
|
* * if calendar range is associated: remove the range
|
|
* * a Calendar change the CalendarRange:
|
|
* * re-create the previous calendar range;
|
|
* * remove the current calendar range
|
|
* * a calendar change the mainUser
|
|
* * cancel the calendar in the previous mainUser
|
|
* * recreate the previous calendar range in the previousMainUser, if any
|
|
* * delete the current calendar range in the current mainUser, if any
|
|
* * create the calendar in the current mainUser
|
|
*
|
|
*/
|
|
|
|
if (!$calendar->hasRemoteId()) {
|
|
$this->createCalendarOnRemote($calendar);
|
|
} else {
|
|
if (null !== $previousMainUser) {
|
|
// cancel event in previousMainUserCalendar
|
|
$this->cancelOnRemote(
|
|
$calendar->getRemoteId(),
|
|
$this->translator->trans('remote_ms_graph.cancel_event_because_main_user_is_%label%', ['%label%' => $calendar->getMainUser()]),
|
|
$previousMainUser,
|
|
'calendar_' . $calendar->getRemoteId()
|
|
);
|
|
$this->createCalendarOnRemote($calendar);
|
|
} else {
|
|
$this->patchCalendarOnRemote($calendar, $newInvites);
|
|
}
|
|
}
|
|
|
|
if ($calendar->hasCalendarRange() && $calendar->getCalendarRange()->hasRemoteId()) {
|
|
$this->removeEvent(
|
|
$calendar->getCalendarRange()->getRemoteId(),
|
|
$calendar->getCalendarRange()->getUser()
|
|
);
|
|
|
|
$calendar->getCalendarRange()
|
|
->addRemoteAttributes([
|
|
'lastModifiedDateTime' => null,
|
|
'changeKey' => null,
|
|
'previousId' => $calendar->getCalendarRange()->getRemoteId(),
|
|
])
|
|
->setRemoteId('');
|
|
}
|
|
|
|
if (null !== $previousCalendarRange) {
|
|
$this->createRemoteCalendarRange($previousCalendarRange);
|
|
}
|
|
}
|
|
|
|
public function syncCalendarRange(CalendarRange $calendarRange): void
|
|
{
|
|
if ($calendarRange->hasRemoteId()) {
|
|
$this->updateRemoteCalendarRange($calendarRange);
|
|
} else {
|
|
$this->createRemoteCalendarRange($calendarRange);
|
|
}
|
|
}
|
|
|
|
public function syncInvite(Invite $invite): void
|
|
{
|
|
if ('' === $remoteId = $invite->getCalendar()->getRemoteId()) {
|
|
return;
|
|
}
|
|
|
|
if (null === $invite->getUser()) {
|
|
return;
|
|
}
|
|
|
|
if (null === $userId = $this->mapCalendarToUser->getUserId($invite->getUser())) {
|
|
return;
|
|
}
|
|
|
|
if ($invite->hasRemoteId()) {
|
|
$remoteIdAttendeeCalendar = $invite->getRemoteId();
|
|
} else {
|
|
$remoteIdAttendeeCalendar = $this->findRemoteIdOnUserCalendar($invite->getCalendar(), $invite->getUser());
|
|
$invite->setRemoteId($remoteIdAttendeeCalendar);
|
|
}
|
|
|
|
switch ($invite->getStatus()) {
|
|
case Invite::PENDING:
|
|
return;
|
|
|
|
case Invite::ACCEPTED:
|
|
$url = "/v1.0/users/{$userId}/calendar/events/{$remoteIdAttendeeCalendar}/accept";
|
|
|
|
break;
|
|
|
|
case Invite::TENTATIVELY_ACCEPTED:
|
|
$url = "/v1.0/users/{$userId}/calendar/events/{$remoteIdAttendeeCalendar}/tentativelyAccept";
|
|
|
|
break;
|
|
|
|
case Invite::DECLINED:
|
|
$url = "/v1.0/users/{$userId}/calendar/events/{$remoteIdAttendeeCalendar}/decline";
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new Exception('not supported');
|
|
}
|
|
|
|
try {
|
|
$this->machineHttpClient->request(
|
|
'POST',
|
|
$url,
|
|
['json' => ['sendResponse' => true]]
|
|
);
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('could not update calendar range to remote', [
|
|
'exception' => $e->getTraceAsString(),
|
|
'content' => $e->getResponse()->getContent(),
|
|
'calendarRangeId' => 'invite_' . $invite->getId(),
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
private function cancelOnRemote(string $remoteId, string $comment, User $user, string $identifier): void
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($user);
|
|
|
|
if (null === $userId) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$this->machineHttpClient->request(
|
|
'POST',
|
|
"users/{$userId}/calendar/events/{$remoteId}/cancel",
|
|
[
|
|
'json' => ['Comment' => $comment],
|
|
]
|
|
);
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('could not update calendar range to remote', [
|
|
'exception' => $e->getTraceAsString(),
|
|
'content' => $e->getResponse()->getContent(),
|
|
'calendarRangeId' => $identifier,
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
private function createCalendarOnRemote(Calendar $calendar): void
|
|
{
|
|
$eventData = $this->remoteEventConverter->calendarToEvent($calendar);
|
|
|
|
[
|
|
'id' => $id,
|
|
'lastModifiedDateTime' => $lastModified,
|
|
'changeKey' => $changeKey
|
|
] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_' . $calendar->getId());
|
|
|
|
if (null === $id) {
|
|
return;
|
|
}
|
|
|
|
$calendar
|
|
->setRemoteId($id)
|
|
->addRemoteAttributes([
|
|
'lastModifiedDateTime' => $lastModified,
|
|
'changeKey' => $changeKey,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param string $identifier an identifier for logging in case of something does not work
|
|
*
|
|
* @return array{?id: string, ?lastModifiedDateTime: int, ?changeKey: string}
|
|
*/
|
|
private function createOnRemote(array $eventData, User $user, string $identifier): array
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($user);
|
|
|
|
if (null === $userId) {
|
|
$this->logger->warning('user does not have userId nor calendarId', [
|
|
'user_id' => $user->getId(),
|
|
'calendar_identifier' => $identifier,
|
|
]);
|
|
|
|
return ['id' => null, 'lastModifiedDateTime' => null, 'changeKey' => null];
|
|
}
|
|
|
|
try {
|
|
$event = $this->machineHttpClient->request(
|
|
'POST',
|
|
'users/' . $userId . '/calendar/events',
|
|
[
|
|
'json' => $eventData,
|
|
]
|
|
)->toArray();
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('could not save calendar range to remote', [
|
|
'exception' => $e->getTraceAsString(),
|
|
'content' => $e->getResponse()->getContent(),
|
|
'calendar_identifier' => $identifier,
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
|
|
return [
|
|
'id' => $event['id'],
|
|
'lastModifiedDateTime' => $this->remoteEventConverter->getLastModifiedDate($event)->getTimestamp(),
|
|
'changeKey' => $event['changeKey'],
|
|
];
|
|
}
|
|
|
|
private function createRemoteCalendarRange(CalendarRange $calendarRange): void
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($calendarRange->getUser());
|
|
|
|
if (null === $userId) {
|
|
$this->logger->warning('user does not have userId nor calendarId', [
|
|
'user_id' => $calendarRange->getUser()->getId(),
|
|
'calendar_range_id' => $calendarRange->getId(),
|
|
]);
|
|
|
|
return;
|
|
}
|
|
|
|
$eventData = $this->remoteEventConverter->calendarRangeToEvent($calendarRange);
|
|
|
|
[
|
|
'id' => $id,
|
|
'lastModifiedDateTime' => $lastModified,
|
|
'changeKey' => $changeKey
|
|
] = $this->createOnRemote(
|
|
$eventData,
|
|
$calendarRange->getUser(),
|
|
'calendar_range_' . $calendarRange->getId()
|
|
);
|
|
|
|
$calendarRange->setRemoteId($id)
|
|
->addRemoteAttributes([
|
|
'lastModifiedDateTime' => $lastModified,
|
|
'changeKey' => $changeKey,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* the remoteId is not the same across different user calendars. This method allow to find
|
|
* the correct remoteId in another calendar.
|
|
*
|
|
* For achieving this, the iCalUid is used.
|
|
*/
|
|
private function findRemoteIdOnUserCalendar(Calendar $calendar, User $user): ?string
|
|
{
|
|
// find the icalUid on original user
|
|
$event = $this->getOnRemote($calendar->getMainUser(), $calendar->getRemoteId());
|
|
$userId = $this->mapCalendarToUser->getUserId($user);
|
|
|
|
if ('' === $iCalUid = ($event['iCalUId'] ?? '')) {
|
|
throw new Exception('no iCalUid for this event');
|
|
}
|
|
|
|
try {
|
|
$events = $this->machineHttpClient->request(
|
|
'GET',
|
|
"/v1.0/users/{$userId}/calendar/events",
|
|
[
|
|
'query' => [
|
|
'$select' => 'id',
|
|
'$filter' => "iCalUId eq '{$iCalUid}'",
|
|
],
|
|
]
|
|
)->toArray();
|
|
} catch (ClientExceptionInterface $clientException) {
|
|
throw $clientException;
|
|
}
|
|
|
|
if (1 !== count($events['value'])) {
|
|
throw new Exception('multiple events found with same iCalUid');
|
|
}
|
|
|
|
return $events['value'][0]['id'];
|
|
}
|
|
|
|
private function getOnRemote(User $user, string $remoteId): array
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($user);
|
|
|
|
if (null === $userId) {
|
|
throw new Exception(
|
|
sprintf(
|
|
'no remote calendar for this user: %s, remoteid: %s',
|
|
$user->getId(),
|
|
$remoteId
|
|
)
|
|
);
|
|
}
|
|
|
|
try {
|
|
return $this->machineHttpClient->request(
|
|
'GET',
|
|
'users/' . $userId . '/calendar/events/' . $remoteId
|
|
)->toArray();
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('Could not get event from calendar', [
|
|
'remoteId' => $remoteId,
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
private function getScheduleTimesForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($user);
|
|
|
|
if (array_key_exists($userId, $this->cacheScheduleTimeForUser)) {
|
|
return $this->cacheScheduleTimeForUser[$userId];
|
|
}
|
|
|
|
if (null === $userId) {
|
|
return [];
|
|
}
|
|
|
|
if (null === $user->getEmailCanonical() || '' === $user->getEmailCanonical()) {
|
|
return [];
|
|
}
|
|
|
|
$body = [
|
|
'schedules' => [$user->getEmailCanonical()],
|
|
'startTime' => [
|
|
'dateTime' => ($startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat())),
|
|
'timeZone' => 'UTC',
|
|
],
|
|
'endTime' => [
|
|
'dateTime' => ($endDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat())),
|
|
'timeZone' => 'UTC',
|
|
],
|
|
];
|
|
|
|
try {
|
|
$response = $this->userHttpClient->request('POST', 'users/' . $userId . '/calendar/getSchedule', [
|
|
'json' => $body,
|
|
])->toArray();
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->debug('Could not get schedule on MSGraph', [
|
|
'error_code' => $e->getResponse()->getStatusCode(),
|
|
'error' => $e->getResponse()->getInfo(),
|
|
]);
|
|
|
|
return [];
|
|
}
|
|
|
|
$this->cacheScheduleTimeForUser[$userId] = array_map(
|
|
fn ($item) => $this->remoteEventConverter->convertAvailabilityToRemoteEvent($item),
|
|
$response['value'][0]['scheduleItems']
|
|
);
|
|
|
|
return $this->cacheScheduleTimeForUser[$userId];
|
|
}
|
|
|
|
private function patchCalendarOnRemote(Calendar $calendar, array $newInvites): void
|
|
{
|
|
$eventDatas = [];
|
|
$eventDatas[] = $this->remoteEventConverter->calendarToEvent($calendar);
|
|
|
|
if (0 < count($newInvites)) {
|
|
// it seems that invitaiton are always send, even if attendee changes are mixed with other datas
|
|
// $eventDatas[] = $this->remoteEventConverter->calendarToEventAttendeesOnly($calendar);
|
|
}
|
|
|
|
foreach ($eventDatas as $eventData) {
|
|
[
|
|
'id' => $id,
|
|
'lastModifiedDateTime' => $lastModified,
|
|
'changeKey' => $changeKey
|
|
] = $this->patchOnRemote(
|
|
$calendar->getRemoteId(),
|
|
$eventData,
|
|
$calendar->getMainUser(),
|
|
'calendar_' . $calendar->getId()
|
|
);
|
|
|
|
$calendar->addRemoteAttributes([
|
|
'lastModifiedDateTime' => $lastModified,
|
|
'changeKey' => $changeKey,
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $identifier an identifier for logging in case of something does not work
|
|
*
|
|
* @return array{?id: string, ?lastModifiedDateTime: int, ?changeKey: string}
|
|
*/
|
|
private function patchOnRemote(string $remoteId, array $eventData, User $user, string $identifier): array
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($user);
|
|
|
|
if (null === $userId) {
|
|
$this->logger->warning('user does not have userId nor calendarId', [
|
|
'user_id' => $user->getId(),
|
|
'calendar_identifier' => $identifier,
|
|
]);
|
|
|
|
return ['id' => null, 'lastModifiedDateTime' => null, 'changeKey' => null];
|
|
}
|
|
|
|
try {
|
|
$event = $this->machineHttpClient->request(
|
|
'PATCH',
|
|
'users/' . $userId . '/calendar/events/' . $remoteId,
|
|
[
|
|
'json' => $eventData,
|
|
]
|
|
)->toArray();
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('could not update calendar range to remote', [
|
|
'exception' => $e->getTraceAsString(),
|
|
'calendarRangeId' => $identifier,
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
|
|
return [
|
|
'id' => $event['id'],
|
|
'lastModifiedDateTime' => $this->remoteEventConverter->getLastModifiedDate($event)->getTimestamp(),
|
|
'changeKey' => $event['changeKey'],
|
|
];
|
|
}
|
|
|
|
private function removeEvent($remoteId, User $user): void
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($user);
|
|
|
|
try {
|
|
$this->machineHttpClient->request(
|
|
'DELETE',
|
|
'users/' . $userId . '/calendar/events/' . $remoteId
|
|
);
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('could not remove event from calendar', [
|
|
'event_remote_id' => $remoteId,
|
|
'user_id' => $user->getId(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function updateRemoteCalendarRange(CalendarRange $calendarRange): void
|
|
{
|
|
$userId = $this->mapCalendarToUser->getUserId($calendarRange->getUser());
|
|
$calendarId = $this->mapCalendarToUser->getCalendarId($calendarRange->getUser());
|
|
|
|
if (null === $userId || null === $calendarId) {
|
|
$this->logger->warning('user does not have userId nor calendarId', [
|
|
'user_id' => $calendarRange->getUser()->getId(),
|
|
'calendar_range_id' => $calendarRange->getId(),
|
|
]);
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$event = $this->machineHttpClient->request(
|
|
'GET',
|
|
'users/' . $userId . '/calendar/events/' . $calendarRange->getRemoteId()
|
|
)->toArray();
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('Could not get event from calendar', [
|
|
'calendar_range_id' => $calendarRange->getId(),
|
|
'calendar_range_remote_id' => $calendarRange->getRemoteId(),
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
|
|
if ($this->remoteEventConverter->getLastModifiedDate($event)->getTimestamp() > $calendarRange->getUpdatedAt()->getTimestamp()) {
|
|
$this->logger->info('Skip updating as the lastModified date seems more fresh than the database one', [
|
|
'calendar_range_id' => $calendarRange->getId(),
|
|
'calendar_range_remote_id' => $calendarRange->getRemoteId(),
|
|
'db_last_updated' => $calendarRange->getUpdatedAt()->getTimestamp(),
|
|
'remote_last_updated' => $this->remoteEventConverter->getLastModifiedDate($event)->getTimestamp(),
|
|
]);
|
|
|
|
return;
|
|
}
|
|
|
|
$eventData = $this->remoteEventConverter->calendarRangeToEvent($calendarRange);
|
|
|
|
try {
|
|
$event = $this->machineHttpClient->request(
|
|
'PATCH',
|
|
'users/' . $userId . '/calendar/events/' . $calendarRange->getRemoteId(),
|
|
[
|
|
'json' => $eventData,
|
|
]
|
|
)->toArray();
|
|
} catch (ClientExceptionInterface $e) {
|
|
$this->logger->warning('could not update calendar range to remote', [
|
|
'exception' => $e->getTraceAsString(),
|
|
'calendarRangeId' => $calendarRange->getId(),
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
|
|
$calendarRange
|
|
->addRemoteAttributes([
|
|
'lastModifiedDateTime' => $this->remoteEventConverter->getLastModifiedDate($event)->getTimestamp(),
|
|
'changeKey' => $event['changeKey'],
|
|
]);
|
|
}
|
|
}
|