<?php
declare(strict_types=1);
namespace Harmonizely\EventSubscriber;
use DateTimeZone;
use Doctrine\ORM\EntityManagerInterface;
use Harmonizely\CalendarEvents;
use Harmonizely\EventTypes\LocationTypeProvider;
use Harmonizely\Integration\Provider\ZoomClientProviderInterface;
use Harmonizely\Integration\Zoom\Exception\CouldNotUpdateZoomMeetingException;
use Harmonizely\Model\EventInterface;
use Harmonizely\Model\EventTypeInterface;
use Harmonizely\Model\Integration;
use Harmonizely\Model\IntegrationInterface;
use Harmonizely\Model\UserInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Webmozart\Assert\Assert;
use Harmonizely\Types\IntegrationType;
final class CreateZoomMeetingSubscriber implements EventSubscriberInterface
{
private $zoomClientProvider;
private $entityManager;
private $logger;
private LocationTypeProvider $locationTypeProvider;
public function __construct(
ZoomClientProviderInterface $zoomClientProvider,
EntityManagerInterface $entityManager,
LoggerInterface $logger,
LocationTypeProvider $locationTypeProvider
) {
$this->zoomClientProvider = $zoomClientProvider;
$this->entityManager = $entityManager;
$this->logger = $logger;
$this->locationTypeProvider = $locationTypeProvider;
}
public static function getSubscribedEvents(): array
{
return [
CalendarEvents::EVENT_PRE_CREATE => 'createMeeting',
CalendarEvents::EVENT_POST_CANCEL => 'cancelMeeting',
CalendarEvents::EVENT_POST_RESCHEDULE => 'updateMeeting',
];
}
public function createMeeting(GenericEvent $event): void
{
/** @var EventInterface $event */
$event = $event->getSubject();
Assert::isInstanceOf($event, EventInterface::class);
$eventType = $event->getEventType();
$user = $event->getUser();
$locationType = $this->locationTypeProvider->provideFor($eventType, $user, $event);
$integration = $this->findIntegration($user);
if (null === $integration || EventTypeInterface::LOCATION_TYPE_ZOOM !== $locationType) {
return;
}
if (!$integration->isEnabled()) {
return;
}
if ($eventType->isGroupMeetingType() && $event->getLocation()) {
return;
}
try {
$zoomClientAdapter = $this->zoomClientProvider->getZoomClient($integration);
$zoomMeeting = $zoomClientAdapter->createMeeting($event);
} catch (\Exception $e) {
$this->logger->critical($e->getMessage());
return;
}
$event->setLocation($zoomMeeting['join_url']);
}
public function cancelMeeting(GenericEvent $genericEvent): void
{
/** @var EventInterface $event */
$event = $genericEvent->getSubject();
Assert::isInstanceOf($event, EventInterface::class);
$eventType = $event->getEventType();
$user = $genericEvent->hasArgument('previousUser')
? $genericEvent->getArgument('previousUser')
: $event->getUser();
$locationType = $this->locationTypeProvider->provideFor($eventType, $user, $event);
$integration = $this->findIntegration($user);
if (null === $integration || EventTypeInterface::LOCATION_TYPE_ZOOM !== $locationType) {
return;
}
if (!$integration->isEnabled()) {
return;
}
try {
$zoomClientAdapter = $this->zoomClientProvider->getZoomClient($integration);
$zoomClientAdapter->cancelMeeting($event);
} catch (\Exception $e) {
$this->logger->critical('Zoom meeting cancellation failed.', [
'message' => $e->getMessage(),
'user_email' => $user->getEmail(),
'invitee_email' => $event->getInvitee()->getEmail(),
]);
}
}
public function updateMeeting(GenericEvent $genericEvent): void
{
/** @var EventInterface $event */
$event = $genericEvent->getSubject();
Assert::isInstanceOf($event, EventInterface::class);
/** @var EventTypeInterface $eventType */
$eventType = $event->getEventType();
$user = $event->getUser();
if ($event->getRescheduling()->getPreviousUser()) {
$genericEvent->setArgument('previousUser', $event->getRescheduling()->getPreviousUser());
$this->cancelMeeting($genericEvent);
$this->createMeeting($genericEvent);
} else {
$locationType = $this->locationTypeProvider->provideFor($eventType, $user, $event);
$integration = $this->findIntegration($user);
if (null === $integration || EventTypeInterface::LOCATION_TYPE_ZOOM !== $locationType) {
return;
}
if (!$integration->isEnabled()) {
return;
}
$eventType = $event->getEventType();
$startTimeInUserTimezone = clone $event->getScheduledAt();
$startTimeInUserTimezone->setTimezone(new DateTimeZone($eventType->getUser()->getTimezone()));
$startsAt = $startTimeInUserTimezone->format('Y-m-d\\TH:i:s');
$invitee = $event->getInvitee();
$topic = $eventType->getName().' - '.$invitee->getFullName();
$duration = $event->getDuration();
if (null === ($zoomLink = $event->getLocation())) {
return;
}
$zoomMeetingId = (int) basename($zoomLink);
try {
$zoomClientAdapter = $this->zoomClientProvider->getZoomClient($integration);
$zoomClientAdapter->updateMeeting($zoomMeetingId, $topic, $duration, $startsAt);
} catch (CouldNotUpdateZoomMeetingException $e) {
$this->logger->critical($e->getMessage());
return;
}
}
}
private function findIntegration(UserInterface $user): ?IntegrationInterface
{
$integrationRepository = $this->entityManager->getRepository(Integration::class);
return $integrationRepository->findOneBy([
'type' => IntegrationType::TYPE_ZOOM,
'user' => $user,
]);
}
}