logger = $logger; $this->machineHttpClient = $machineHttpClient; $this->userRepository = $userRepository; } public function handleCalendarSync(Calendar $calendar, array $notification, User $user): void { switch ($notification['changeType']) { case 'deleted': $this->handleDeleteCalendar($calendar, $notification, $user); break; case 'updated': $this->handleUpdateCalendar($calendar, $notification, $user); break; 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(__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(__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($email); $user = $this->userRepository->findOneByUsernameOrEmail($email); if (null === $user) { continue; } if (!$calendar->isInvited($user)) { $calendar->addUser($user); } $invite = $calendar->getInviteForUser($user); switch ($status) { // possible cases: none, organizer, tentativelyAccepted, accepted, declined, notResponded. case 'none': case 'notResponded': $invite->setStatus(Invite::PENDING); break; case 'tentativelyAccepted': $invite->setStatus(Invite::TENTATIVELY_ACCEPTED); break; case 'accepted': $invite->setStatus(Invite::ACCEPTED); break; case 'declined': $invite->setStatus(Invite::DECLINED); break; default: throw new LogicException('should not happens, not implemented: ' . $status); break; } } foreach ($calendar->getUsers() as $user) { if (!in_array(strtolower($user->getEmailCanonical()), $emails, true)) { $calendar->removeUser($user); } } } }