Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
digitalascetic/google-api-client / Service / GoogleCalendarService.php
Size: Mime:
<?php
/**
 * Created by PhpStorm.
 * User: eduarddezacastellano
 * Date: 12/09/2018
 * Time: 16:36
 */

namespace DigitalAscetic\GoogleApiClientBundle\Service;


use DigitalAscetic\GoogleApiClientBundle\Entity\GoogleApiNotificationsChannel;
use DigitalAscetic\GoogleApiClientBundle\Entity\IGoogleApiCalendarEvent;
use DigitalAscetic\GoogleApiClientBundle\Entity\IGoogleApiUser;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;


class GoogleCalendarService implements IGoogleApiService
{
    const SERVICE_NAME = 'ascetic_google_api_client.calendar';
    const SERVICE_ENABLED_PROPERTY = 'ascetic_google_api_client.calendar.enabled';
    const SERVICE_WATCH_ENABLED_PROPERTY = 'ascetic_google_api_client.calendar.watch.enabled';

    /** @var bool */
    private $enabled;

    /** @var bool */
    private $watching;

    /** @var ContainerInterface $container */
    private $container;

    /** @var EntityManagerInterface $em */
    private $em;

    /** @var GoogleAuthService $googleAuthService */
    private $googleAuthService;

    /** @var GoogleNotificationsService */
    private $notificationService;

    /** @var IGoogleApiEventsService $eventsService */
    private $eventsService;

    /** @var LoggerInterface */
    private $logger;

    /**
     * GoogleApiCalendarService constructor.
     * @param ContainerInterface $container
     * @param EntityManagerInterface $em
     * @param GoogleAuthService $googleAuthService
     * @param GoogleNotificationsService $notificationsService
     * @param LoggerInterface $logger
     * @param IGoogleApiEventsService $eventsService
     */
    public function __construct(ContainerInterface         $container,
                                EntityManagerInterface     $em,
                                GoogleAuthService          $googleAuthService,
                                GoogleNotificationsService $notificationsService,
                                LoggerInterface            $logger,
                                IGoogleApiEventsService    $eventsService = null)
    {
        $this->container = $container;
        $this->googleAuthService = $googleAuthService;
        $this->em = $em;
        $this->notificationService = $notificationsService;
        $this->eventsService = $eventsService;
        $this->logger = $logger;
        $this->enabled = $container->getParameter(GoogleCalendarService::SERVICE_ENABLED_PROPERTY);
        $this->watching = $container->getParameter(GoogleCalendarService::SERVICE_WATCH_ENABLED_PROPERTY);
    }

    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    public function isWatching(): bool
    {
        return $this->watching;
    }

    /**
     * @param IGoogleApiUser $user
     * @param IGoogleApiCalendarEvent $event
     * @param string $calendarId
     * @return \Google_Service_Calendar_Event
     * @throws \Google_Exception
     */
    public function getEvent(IGoogleApiUser $user, IGoogleApiCalendarEvent $event, $calendarId = 'primary')
    {
        if ($this->googleAuthService->hasAuthorization($user) &&
            $event->isSyncableGoogleCalendar()) {

            /** @var \Google_Service_Calendar $calendarService */
            $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user));

            return $calendarService->events->get($calendarId, $event->getGoogleEventId());
        }
    }


    /**
     * @param IGoogleApiUser $user
     * @param string $calendarId
     * @param GoogleApiNotificationsChannel|null $channel
     * @param string|null $host
     * @return \Google_Service_Calendar_Event[]|null
     * @throws \Google_Exception
     */
    public function listEvents(IGoogleApiUser $user, $calendarId = 'primary', GoogleApiNotificationsChannel $channel = null, string $host = null)
    {
        if ($this->googleAuthService->hasAuthorization($user)) {

            /** @var \Google_Service_Calendar $calendarService */
            $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user, $host));

            $optParams = array();

            if (isset($channel) && $channel->getLastNotificationHandledDate()) {
                $optParams['updatedMin'] = $channel->getLastNotificationHandledDate()->format(\DateTime::RFC3339);
            }

            if (isset($channel) && $channel->getSyncToken() && $channel->getLastNotificationHandledDate() == null) {
                $optParams['syncToken'] = $channel->getSyncToken();
            }

            /** https://developers.google.com/calendar/api/v3/reference/events/list?hl=es-419 */
            $optParams['eventTypes'] = 'default';

            try {
                $googleEvents = $calendarService->events->listEvents($calendarId, $optParams);
            } catch (\Exception $exception) {
                $this->logger->error('GOOGLE_API_CLIENT::listEvents(): ' . $exception->getMessage());
                /**
                 * If the syncToken expires, the server will respond with a 410 GONE response code
                 * and the client should clear its storage and perform a full synchronization without any syncToken.
                 */
                if ($exception->getCode() === 410) {
                    if ($channel) {
                        $this->updateSyncTokenChannel($channel, null);
                    }

                    $googleEvents = $calendarService->events->listEvents($calendarId);
                }
            }

            $events = array();

            if (isset($channel) && !empty($googleEvents)) {
                $this->updateSyncTokenChannel($channel, $googleEvents->getNextSyncToken());
            }

            if (!empty($googleEvents)) {
                /** @var \Google_Service_Calendar_Event[] $events */
                $events = $googleEvents->getItems();
            }

            return $events;
        }

        return null;
    }

    public function insertEvent(IGoogleApiUser $user, IGoogleApiCalendarEvent $event, $calendarId = 'primary', string $host = null)
    {
        if ($this->googleAuthService->hasAuthorization($user) &&
            $event->isSyncableGoogleCalendar()) {

            /** @var \Google_Service_Calendar $calendarService */
            $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user, $host));

            $newEvent = new \Google_Service_Calendar_Event(array(
                'summary' => $event->getSummary(),
                'location' => $event->getLocation(),
                'description' => $event->getDescription(),
                'start' => $event->getStart(),
                'end' => $event->getEnd()
            ));

            $googleEvent = $calendarService->events->insert($calendarId, $newEvent);

            $event->setGoogleEventId($googleEvent->getId());
            $this->em->persist($event);
            $this->em->flush();
        }
    }

    public function patchEvent(IGoogleApiUser $user, IGoogleApiCalendarEvent $event, $calendarId = 'primary', string $host = null)
    {
        if ($this->googleAuthService->hasAuthorization($user) &&
            $event->isSyncableGoogleCalendar()) {

            /** @var \Google_Service_Calendar $calendarService */
            $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user, $host));

            $newEvent = new \Google_Service_Calendar_Event(array(
                'summary' => $event->getSummary(),
                'location' => $event->getLocation(),
                'description' => $event->getDescription(),
                'start' => $event->getStart(),
                'end' => $event->getEnd()
            ));

            $calendarService->events->patch($calendarId, $event->getGoogleEventId(), $newEvent);
        }
    }

    public function deleteEvent(IGoogleApiUser $user, IGoogleApiCalendarEvent $event, $calendarId = 'primary')
    {
        if ($this->googleAuthService->hasAuthorization($user) &&
            $event->isSyncableGoogleCalendar()) {

            /** @var \Google_Service_Calendar $calendarService */
            $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user));

            $calendarService->events->delete($calendarId, $event->getGoogleEventId());
        }
    }

    public function getNotifications(GoogleApiNotificationsChannel $channel, string $host = null)
    {
        if ($this->isWatching() && $channel->getToken() === GoogleApiNotificationsChannel::CALENDAR_TOKEN) {

            $googleEvents = $this->listEvents($channel->getUser(), 'primary', $channel, $host);

            if (!empty($googleEvents)) {

                $batchProcesses = 1;

                $eventsIds = array_map(function (\Google_Service_Calendar_Event $event) {
                    return $event->getId();
                }, $googleEvents);

                $calendarEvents = $this->eventsService->getEventsByGoogleEventsId($eventsIds);

                foreach ($calendarEvents as $calendarEvent) {
                    if ($calendarEvent) {

                        $googleEventFiltered = array_filter($googleEvents, function (\Google_Service_Calendar_Event $event) use ($calendarEvent) {
                            return $event->getId() === $calendarEvent->getGoogleEventId();
                        });

                        $googleEvent = !empty($googleEventFiltered) ? $googleEventFiltered[0] : null;

                        if (isset($googleEvent)) {
                            $calendarEvent = $this->updateGoogleApiCalendarEvent($calendarEvent, $googleEvent);
                            $this->em->persist($calendarEvent);
                        }

                        if ($batchProcesses % 20 === 0) {
                            $this->em->flush();
                            $this->em->clear();
                        }

                        $batchProcesses++;
                    }
                }

                $this->em->flush();
            }
        }
    }

    public function syncEvent(IGoogleApiUser $user, IGoogleApiCalendarEvent $calendarEvent)
    {
        if ($calendarEvent->isSyncableGoogleCalendar()) {
            $googleEvent = $this->getEvent($user, $calendarEvent);

            if (isset($googleEvent)) {
                $this->updateGoogleApiCalendarEvent($calendarEvent, $googleEvent);
                $this->em->persist($calendarEvent);
                $this->em->flush();
            }
        }
    }

    public function disconnect(IGoogleApiUser $user, string $host = null)
    {
        $client = $this->googleAuthService->getClientAuthenticatedForUser($user, $host);
        $client->setUseBatch(true);

        /** @var \Google_Service_Calendar $calendarService */
        $calendarService = new \Google_Service_Calendar($client);

        $batch = $calendarService->createBatch();

        /** @var IGoogleApiCalendarEvent[] $events */
        $events = $this->eventsService->getEventsByUser($user);

        $batchProcesses = 1;
        foreach ($events as $event) {
            $eventId = $event->getGoogleEventId();
            $batch->add($calendarService->events->delete($event->getGoogleCalendarId(), $eventId));

            // clear GoogleEventId
            $event->setGoogleEventId(null);
            $this->em->persist($event);

            if ($batchProcesses % 20 === 0) {
                $this->em->flush();
                $this->em->clear();
            }

            $batchProcesses++;
        }

        $this->em->flush();

        try {
            $batch->execute();
        } catch (\Exception $exception) {
            $this->logger->error('GOOGLE DISCONNECT ERROR WITH CODE: ' . $exception->getCode() . ' AND MESSAGE: ' . $exception->getMessage());
        }

        $client->setUseBatch(false);
    }

    public function watchResource(IGoogleApiUser $user, string $host = null)
    {
        if ($this->isWatching()) {
            /** @var \Google_Service_Calendar $calendarService */
            $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user, $host));

            $this->createNewCalendarNotificationsChannel($calendarService, $user, 'primary', $host);
        }
    }

    public function renewWatchChannels(IGoogleApiUser $user, string $host = null)
    {

        /** @var \Google_Service_Calendar $calendarService */
        $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user, $host));

        $channels = $this->notificationService->getChannelsByUser($user);

        $batchProcesses = 1;
        foreach ($channels as $key => $value) {
            if ($channels[$key]->isExpired()) {
                $this->em->remove($channels[$key]);
                unset($channels[$key]);

                if ($batchProcesses % 20 === 0) {
                    $this->em->flush();
                    $this->em->clear();
                }

                $batchProcesses++;
            }
        }

        if ($batchProcesses > 1) {
            $this->em->flush();
        }

        // IF ALL CHANNELS ARE EXPIRED; WE CREATE A NEW CHANNEL
        if ($this->isWatching() && count($channels) <= 0) {
            $this->createNewCalendarNotificationsChannel($calendarService, $user, 'primary', $host);
        }
    }

    public function stopWatchingResources(IGoogleApiUser $user, string $host = null)
    {
        /** @var \Google_Service_Calendar $calendarService */
        $calendarService = new \Google_Service_Calendar($this->googleAuthService->getClientAuthenticatedForUser($user));

        /** @var GoogleApiNotificationsChannel[] $channels */
        $channels = $this->notificationService->getChannelsByUser($user);

        $batchProcesses = 1;

        foreach ($channels as $channel) {
            $channelParams = new \Google_Service_Calendar_Channel();
            $channelParams->setId($channel->getChannelId());
            $channelParams->setResourceId($channel->getResourceId());

            $channelParams->setType($channel->getType());
            $channelParams->setToken($channel->getToken());
            $channelParams->setAddress($this->notificationService->getDeliveryAddress($host));

            try {
                $calendarService->channels->stop($channelParams);
            } catch (\Exception $exception) {
                $this->logger->error('GOOGLE STOP CHANNEL ERROR WITH CODE: ' . $exception->getCode() . ' AND MESSAGE: ' . $exception->getMessage());
            }

            $this->em->remove($channel);

            if ($batchProcesses % 20 === 0) {
                $this->em->flush();
                $this->em->clear();
            }

            $batchProcesses++;
        }

        $this->em->flush();
    }

    private function createNewCalendarNotificationsChannel(\Google_Service_Calendar $calendarService, IGoogleApiUser $user, string $calendarId = 'primary', string $host = null)
    {
        if (!$user->isPersisted()) {
            $this->logger->error('Error creating GoogleApiNotificationsChannel for user: ' . $user . '. User is not persisted.');
            return false;
        }

        $newChannel = new GoogleApiNotificationsChannel(GoogleApiNotificationsChannel::TYPE_WEB_HOOK, GoogleApiNotificationsChannel::CALENDAR_TOKEN, $user);
        $channelParams = new \Google_Service_Calendar_Channel();
        $channelParams->setId($newChannel->getChannelId());
        $channelParams->setType($newChannel->getType());
        $channelParams->setToken($newChannel->getToken());
        $channelParams->setAddress($this->notificationService->getDeliveryAddress($host));

        $optParams = [];

        /** https://developers.google.com/calendar/api/v3/reference/events/watch?hl=es-419 */
        $optParams['eventTypes'] = 'default';

        try {
            $channel = $calendarService->events->watch($calendarId, $channelParams, $optParams);
        } catch (\Throwable $exception) {
            $this->logger->error('Google Calendar Api Watch method, error with code: ' . $exception->getCode() . ' and message: ' . $exception->getMessage());
            return false;
        }

        $newChannel->setResourceId($channel->getResourceId());
        $newChannel->setExpiration($channel->getExpiration());

        $this->em->persist($newChannel);
        $this->em->flush();

        return $newChannel;
    }

    private function updateGoogleApiCalendarEvent(IGoogleApiCalendarEvent $calendarEvent, \Google_Service_Calendar_Event $event)
    {
        $calendarEvent->setSummary($event->getSummary());
        $calendarEvent->setLocation($event->getLocation());
        $calendarEvent->setDescription($event->getDescription());

        if ($event->getStart()) {
            $calendarEvent->setStart($event->getStart());
        }

        if ($event->getEnd()) {
            $calendarEvent->setEnd($event->getEnd());
        }

        return $calendarEvent;
    }

    private function updateSyncTokenChannel(GoogleApiNotificationsChannel $channel, ?string $syncToken)
    {
        $channel->setSyncToken($syncToken);
        $this->em->persist($channel);
        $this->em->flush();
    }
}