mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-28 02:23:51 +00:00
refactor and rename classes
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
|
||||
|
||||
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
|
||||
use LogicException;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
|
||||
|
||||
class MachineHttpClient implements HttpClientInterface
|
||||
{
|
||||
use BearerAuthorizationTrait;
|
||||
|
||||
private HttpClientInterface $decoratedClient;
|
||||
|
||||
private MachineTokenStorage $machineTokenStorage;
|
||||
|
||||
/**
|
||||
* @param HttpClientInterface $decoratedClient
|
||||
*/
|
||||
public function __construct(MachineTokenStorage $machineTokenStorage, ?HttpClientInterface $decoratedClient = null)
|
||||
{
|
||||
$this->decoratedClient = $decoratedClient ?? \Symfony\Component\HttpClient\HttpClient::create();
|
||||
$this->machineTokenStorage = $machineTokenStorage;
|
||||
}
|
||||
|
||||
public function request(string $method, string $url, array $options = []): ResponseInterface
|
||||
{
|
||||
$options['headers'] = array_merge(
|
||||
$options['headers'] ?? [],
|
||||
$this->getAuthorizationHeaders($this->machineTokenStorage->getToken())
|
||||
);
|
||||
$options['base_uri'] = 'https://graph.microsoft.com/v1.0/';
|
||||
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
case 'HEAD':
|
||||
$options['headers']['Accept'] = 'application/json';
|
||||
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
$options['headers']['Content-Type'] = 'application/json';
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new LogicException("Method not supported: {$method}");
|
||||
}
|
||||
|
||||
return $this->decoratedClient->request($method, $url, $options);
|
||||
}
|
||||
|
||||
public function stream($responses, ?float $timeout = null): ResponseStreamInterface
|
||||
{
|
||||
return $this->decoratedClient->stream($responses, $timeout);
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
|
||||
|
||||
use Chill\MainBundle\Redis\ChillRedis;
|
||||
use League\OAuth2\Client\Token\AccessTokenInterface;
|
||||
use TheNetworg\OAuth2\Client\Provider\Azure;
|
||||
use TheNetworg\OAuth2\Client\Token\AccessToken;
|
||||
|
||||
class MachineTokenStorage
|
||||
{
|
||||
private const KEY = 'msgraph_access_token';
|
||||
|
||||
private ?AccessTokenInterface $accessToken = null;
|
||||
|
||||
private Azure $azure;
|
||||
|
||||
private ChillRedis $chillRedis;
|
||||
|
||||
public function __construct(Azure $azure, ChillRedis $chillRedis)
|
||||
{
|
||||
$this->azure = $azure;
|
||||
$this->chillRedis = $chillRedis;
|
||||
}
|
||||
|
||||
public function getToken(): AccessTokenInterface
|
||||
{
|
||||
if (null === $this->accessToken || $this->accessToken->hasExpired()) {
|
||||
$this->accessToken = $this->azure->getAccessToken('client_credentials', [
|
||||
'scope' => 'https://graph.microsoft.com/.default',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function storeToken(AccessToken $token): void
|
||||
{
|
||||
$this->chillRedis->set(self::KEY, serialize($token));
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
|
||||
|
||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraphRemoteCalendarConnector;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Write metadata to user, which allow to find his default calendar.
|
||||
*/
|
||||
class MapCalendarToUser
|
||||
{
|
||||
public const METADATA_KEY = 'msgraph';
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
private MSGraphRemoteCalendarConnector $remoteCalendarConnector;
|
||||
|
||||
public function __construct(MSGraphRemoteCalendarConnector $remoteCalendarConnector, LoggerInterface $logger)
|
||||
{
|
||||
$this->remoteCalendarConnector = $remoteCalendarConnector;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function getCalendarId(User $user): ?string
|
||||
{
|
||||
if (null === $mskey = ($user->getAttributes()[self::METADATA_KEY] ?? null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $msKey['defaultCalendarId'] ?? null;
|
||||
}
|
||||
|
||||
public function writeMetadata(User $user): User
|
||||
{
|
||||
if (null === $userData = $this->remoteCalendarConnector->getUserByEmail($user->getEmailCanonical())) {
|
||||
$this->logger->warning('[MapCalendarToUser] could find user on msgraph', ['userId' => $user->getId(), 'email' => $user->getEmailCanonical()]);
|
||||
|
||||
return $this->writeNullData($user);
|
||||
}
|
||||
|
||||
if (null === $defaultCalendar = $this->remoteCalendarConnector->getDefaultUserCalendar($userData['id'])) {
|
||||
$this->logger->warning('[MapCalendarToUser] could find default calendar', ['userId' => $user->getId(), 'email' => $user->getEmailCanonical()]);
|
||||
|
||||
return $this->writeNullData($user);
|
||||
}
|
||||
|
||||
return $user->setAttributes([self::METADATA_KEY => [
|
||||
'id' => $userData['id'],
|
||||
'userPrincipalName' => $userData['userPrincipalName'],
|
||||
'defaultCalendarId' => $defaultCalendar['id'],
|
||||
]]);
|
||||
}
|
||||
|
||||
private function writeNullData(User $user): User
|
||||
{
|
||||
return $user->unsetAttribute(self::METADATA_KEY);
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
|
||||
|
||||
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
|
||||
use LogicException;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
|
||||
|
||||
class OnBehalfOfUserHttpClient
|
||||
{
|
||||
use BearerAuthorizationTrait;
|
||||
|
||||
private HttpClientInterface $decoratedClient;
|
||||
|
||||
private OnBehalfOfUserTokenStorage $tokenStorage;
|
||||
|
||||
/**
|
||||
* @param HttpClientInterface $decoratedClient
|
||||
*/
|
||||
public function __construct(OnBehalfOfUserTokenStorage $tokenStorage, ?HttpClientInterface $decoratedClient = null)
|
||||
{
|
||||
$this->decoratedClient = $decoratedClient ?? \Symfony\Component\HttpClient\HttpClient::create();
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
}
|
||||
|
||||
public function request(string $method, string $url, array $options = []): ResponseInterface
|
||||
{
|
||||
$options['headers'] = array_merge(
|
||||
$options['headers'] ?? [],
|
||||
$this->getAuthorizationHeaders($this->tokenStorage->getToken())
|
||||
);
|
||||
$options['base_uri'] = 'https://graph.microsoft.com/v1.0/';
|
||||
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
case 'HEAD':
|
||||
$options['headers']['Accept'] = 'application/json';
|
||||
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
$options['headers']['Content-Type'] = 'application/json';
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new LogicException("Method not supported: {$method}");
|
||||
}
|
||||
|
||||
return $this->decoratedClient->request($method, $url, $options);
|
||||
}
|
||||
|
||||
public function stream($responses, ?float $timeout = null): ResponseStreamInterface
|
||||
{
|
||||
return $this->decoratedClient->stream($responses, $timeout);
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
|
||||
|
||||
use LogicException;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use TheNetworg\OAuth2\Client\Provider\Azure;
|
||||
use TheNetworg\OAuth2\Client\Token\AccessToken;
|
||||
|
||||
/**
|
||||
* Store token obtained on behalf of a User.
|
||||
*/
|
||||
class OnBehalfOfUserTokenStorage
|
||||
{
|
||||
public const MS_GRAPH_ACCESS_TOKEN = 'msgraph_access_token';
|
||||
|
||||
private Azure $azure;
|
||||
|
||||
private SessionInterface $session;
|
||||
|
||||
public function __construct(Azure $azure, SessionInterface $session)
|
||||
{
|
||||
$this->azure = $azure;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function getToken(): AccessToken
|
||||
{
|
||||
/** @var ?AccessToken $token */
|
||||
$token = $this->session->get(self::MS_GRAPH_ACCESS_TOKEN, null);
|
||||
|
||||
if (null === $token) {
|
||||
throw new LogicException('unexisting token');
|
||||
}
|
||||
|
||||
if ($token->hasExpired()) {
|
||||
$token = $this->azure->getAccessToken('refresh_token', [
|
||||
'refresh_token' => $token->getRefreshToken(),
|
||||
]);
|
||||
|
||||
$this->setToken($token);
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function hasToken(): bool
|
||||
{
|
||||
return $this->session->has(self::MS_GRAPH_ACCESS_TOKEN);
|
||||
}
|
||||
|
||||
public function setToken(AccessToken $token): void
|
||||
{
|
||||
$this->session->set(self::MS_GRAPH_ACCESS_TOKEN, $token);
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
|
||||
|
||||
use Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
|
||||
/**
|
||||
* Convert Chill Calendar event to Remote MS Graph event, and MS Graph
|
||||
* event to RemoteEvent.
|
||||
*/
|
||||
class RemoteEventConverter
|
||||
{
|
||||
private const REMOTE_DATE_FORMAT = 'Y-m-d\TH:i:s.u0';
|
||||
|
||||
private DateTimeZone $defaultDateTimeZone;
|
||||
|
||||
private DateTimeZone $remoteDateTimeZone;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->defaultDateTimeZone = (new DateTimeImmutable())->getTimezone();
|
||||
$this->remoteDateTimeZone = new DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
public function convertToRemote(array $event): RemoteEvent
|
||||
{
|
||||
$startDate =
|
||||
DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['start']['dateTime'])
|
||||
->setTimezone($this->defaultDateTimeZone);
|
||||
$endDate =
|
||||
DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['end']['dateTime'], $this->remoteDateTimeZone)
|
||||
->setTimezone($this->defaultDateTimeZone);
|
||||
|
||||
return new RemoteEvent(
|
||||
$event['id'],
|
||||
$event['subject'],
|
||||
'',
|
||||
$startDate,
|
||||
$endDate
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector;
|
||||
|
||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MachineHttpClient;
|
||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\OnBehalfOfUserHttpClient;
|
||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\OnBehalfOfUserTokenStorage;
|
||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteEventConverter;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use DateTimeImmutable;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||
{
|
||||
private MachineHttpClient $machineHttpClient;
|
||||
|
||||
private RemoteEventConverter $remoteEventConverter;
|
||||
|
||||
private OnBehalfOfUserTokenStorage $tokenStorage;
|
||||
|
||||
private UrlGeneratorInterface $urlGenerator;
|
||||
|
||||
private OnBehalfOfUserHttpClient $userHttpClient;
|
||||
|
||||
public function __construct(
|
||||
MachineHttpClient $machineHttpClient,
|
||||
OnBehalfOfUserTokenStorage $tokenStorage,
|
||||
OnBehalfOfUserHttpClient $userHttpClient,
|
||||
RemoteEventConverter $remoteEventConverter,
|
||||
UrlGeneratorInterface $urlGenerator
|
||||
) {
|
||||
$this->machineHttpClient = $machineHttpClient;
|
||||
$this->remoteEventConverter = $remoteEventConverter;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->userHttpClient = $userHttpClient;
|
||||
}
|
||||
|
||||
public function getDefaultUserCalendar(string $idOrUserPrincipalName): ?array
|
||||
{
|
||||
$value = $this->machineHttpClient->request('GET', "users/{$idOrUserPrincipalName}/calendars", [
|
||||
'query' => ['$filter' => 'isDefaultCalendar eq true'],
|
||||
])->toArray()['value'];
|
||||
|
||||
return $value[0] ?? null;
|
||||
}
|
||||
|
||||
public function getMakeReadyResponse(string $returnPath): Response
|
||||
{
|
||||
return new RedirectResponse($this->urlGenerator
|
||||
->generate('chill_calendar_remote_connect_azure', ['returnPath' => $returnPath]));
|
||||
}
|
||||
|
||||
public function getUserByEmail(string $email): ?array
|
||||
{
|
||||
$value = $this->machineHttpClient->request('GET', 'users', [
|
||||
'query' => ['$filter' => "mail eq '{$email}'"],
|
||||
])->toArray()['value'];
|
||||
|
||||
return $value[0] ?? null;
|
||||
}
|
||||
|
||||
public function isReady(): bool
|
||||
{
|
||||
return $this->tokenStorage->hasToken();
|
||||
}
|
||||
|
||||
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array
|
||||
{
|
||||
$bareEvents = $this->userHttpClient->request(
|
||||
'GET',
|
||||
'users/c4f1fcc7-10e4-4ea9-89ac-c89a00e0a51a/calendarView',
|
||||
[
|
||||
'query' => [
|
||||
'startDateTime' => $startDate->format(DateTimeImmutable::ATOM),
|
||||
'endDateTime' => $endDate->format(DateTimeImmutable::ATOM),
|
||||
'$select' => 'id,subject,start,end',
|
||||
],
|
||||
]
|
||||
)->toArray();
|
||||
|
||||
return array_map(function ($item) { return $this->remoteEventConverter->convertToRemote($item); }, $bareEvents['value']);
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use DateTimeImmutable;
|
||||
use LogicException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||
{
|
||||
public function getMakeReadyResponse(string $returnPath): Response
|
||||
{
|
||||
throw new LogicException('As this connector is always ready, this method should not be called');
|
||||
}
|
||||
|
||||
public function isReady(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?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);
|
||||
|
||||
namespace Chill\CalendarBundle\RemoteCalendar\Connector;
|
||||
|
||||
use Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use DateTimeImmutable;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
interface RemoteCalendarConnectorInterface
|
||||
{
|
||||
/**
|
||||
* Return a response, more probably a RedirectResponse, where the user
|
||||
* will be able to fullfill requirements to prepare this connector and
|
||||
* make it ready.
|
||||
*/
|
||||
public function getMakeReadyResponse(string $returnPath): Response;
|
||||
|
||||
/**
|
||||
* Return true if the connector is ready to act as a proxy for reading
|
||||
* remote calendars.
|
||||
*/
|
||||
public function isReady(): bool;
|
||||
|
||||
/**
|
||||
* @return array|RemoteEvent[]
|
||||
*/
|
||||
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array;
|
||||
}
|
Reference in New Issue
Block a user