$this->handleDeleteCalendar($calendar, $notification, $user), 'updated' => $this->handleUpdateCalendar($calendar, $notification, $user), default => throw new RuntimeException('this change type is not supported: ' . $notification['changeType']), }; } private function handleDeleteCalendar(Calendar $calendar, array $notification, User $user): void { $calendar ->setStatus(Calendar::STATUS_CANCELED) ->setCalendarRange(null); $calendar->preventEnqueueChanges = true; } private function handleUpdateCalendar(Calendar $calendar, array $notification, User $user): void { try { $new = $this->machineHttpClient->request( 'GET', $notification['resource'] )->toArray(); } catch (ClientExceptionInterface $clientException) { $this->logger->warning(self::class . ' could not retrieve event from ms graph. Already deleted ?', [ 'calendarId' => $calendar->getId(), 'remoteEventId' => $notification['resource'], ]); throw $clientException; } if (false === $new['isOrganizer']) { return; } $lastModified = RemoteEventConverter::convertStringDateWithTimezone( $new['lastModifiedDateTime'] ); if ($calendar->getRemoteAttributes()['lastModifiedDateTime'] === $lastModified->getTimestamp()) { $this->logger->info(self::class . ' change key is equals. Source is probably a local update', [ 'calendarRangeId' => $calendar->getId(), 'remoteEventId' => $notification['resource'], ]); return; } $this->syncAttendees($calendar, $new['attendees']); $startDate = RemoteEventConverter::convertStringDateWithoutTimezone($new['start']['dateTime']); $endDate = RemoteEventConverter::convertStringDateWithoutTimezone($new['end']['dateTime']); if ($startDate->getTimestamp() !== $calendar->getStartDate()->getTimestamp()) { $calendar->setStartDate($startDate); } if ($endDate->getTimestamp() !== $calendar->getEndDate()->getTimestamp()) { $calendar->setEndDate($endDate); } $calendar ->addRemoteAttributes([ 'lastModifiedDateTime' => $lastModified->getTimestamp(), 'changeKey' => $new['changeKey'], ]) ->preventEnqueueChanges = true; } private function syncAttendees(Calendar $calendar, array $attendees): void { $emails = []; foreach ($attendees as $attendee) { $status = $attendee['status']['response']; if ('organizer' === $status) { continue; } $email = $attendee['emailAddress']['address']; $emails[] = strtolower((string) $email); $user = $this->userRepository->findOneByUsernameOrEmail($email); if (null === $user) { continue; } if (!$calendar->isInvited($user)) { $calendar->addUser($user); } $invite = $calendar->getInviteForUser($user); match ($status) { 'none', 'notResponded' => $invite->setStatus(Invite::PENDING), 'tentativelyAccepted' => $invite->setStatus(Invite::TENTATIVELY_ACCEPTED), 'accepted' => $invite->setStatus(Invite::ACCEPTED), 'declined' => $invite->setStatus(Invite::DECLINED), default => throw new LogicException('should not happens, not implemented: ' . $status), }; } foreach ($calendar->getUsers() as $user) { if (!in_array(strtolower($user->getEmailCanonical()), $emails, true)) { $calendar->removeUser($user); } } } }