mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-12 13:24:25 +00:00
wip: synchro
This commit is contained in:
parent
9935af0497
commit
811798e23f
@ -3,48 +3,49 @@
|
|||||||
namespace Chill\CalendarBundle\Command;
|
namespace Chill\CalendarBundle\Command;
|
||||||
|
|
||||||
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MachineTokenStorage;
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MachineTokenStorage;
|
||||||
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MapCalendarToUser;
|
||||||
use Chill\CalendarBundle\Synchro\Connector\MSGraphRemoteCalendarConnector;
|
use Chill\CalendarBundle\Synchro\Connector\MSGraphRemoteCalendarConnector;
|
||||||
use Chill\MainBundle\Repository\UserRepository;
|
use Chill\MainBundle\Repository\UserRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
class MapUserCalendarCommand extends Command
|
class MapUserCalendarCommand extends Command
|
||||||
{
|
{
|
||||||
private MSGraphRemoteCalendarConnector $remoteCalendarConnector;
|
private EntityManagerInterface $em;
|
||||||
|
|
||||||
|
private MapCalendarToUser $mapCalendarToUser;
|
||||||
|
|
||||||
private UserRepository $userRepository;
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
public function __construct(MSGraphRemoteCalendarConnector $remoteCalendarConnector)
|
public function __construct(EntityManagerInterface $em, MapCalendarToUser $mapCalendarToUser, UserRepository $userRepository)
|
||||||
{
|
{
|
||||||
parent::__construct('chill:calendar:map-user');
|
parent::__construct('chill:calendar:map-user');
|
||||||
|
|
||||||
$this->remoteCalendarConnector = $remoteCalendarConnector;
|
$this->em = $em;
|
||||||
|
$this->mapCalendarToUser = $mapCalendarToUser;
|
||||||
|
$this->userRepository = $userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
$limit = 2;
|
$limit = 2;
|
||||||
|
$offset = 0;
|
||||||
|
$total = $this->userRepository->countByNotHavingAttribute(MapCalendarToUser::METADATA_KEY);
|
||||||
|
|
||||||
do {
|
while ($offset < $total) {
|
||||||
$users = $this->userRepository->findByNotHavingAttribute('ms:graph', $limit);
|
$users = $this->userRepository->findByNotHavingAttribute(MapCalendarToUser::METADATA_KEY, $limit, $offset);
|
||||||
|
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
$usersData = $this->remoteCalendarConnector->getUserByEmail($user->getEmailCanonical());
|
$this->mapCalendarToUser->writeMetadata($user);
|
||||||
|
$offset++;
|
||||||
$defaultCalendar
|
|
||||||
|
|
||||||
$user->setAttributes(['ms:graph' => [
|
|
||||||
|
|
||||||
]]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (count($users) === $limit);
|
$this->em->flush();
|
||||||
|
$this->em->clear();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ class RemoteCalendarConnectAzureController
|
|||||||
return $this->clientRegistry
|
return $this->clientRegistry
|
||||||
->getClient('azure') // key used in config/packages/knpu_oauth2_client.yaml
|
->getClient('azure') // key used in config/packages/knpu_oauth2_client.yaml
|
||||||
->redirect([
|
->redirect([
|
||||||
'User.Read', 'Calendars.Read', 'Calendars.Read.Shared',
|
'https://graph.microsoft.com/.default'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,16 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\CalendarBundle\Controller;
|
namespace Chill\CalendarBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\RemoteEventConverter;
|
||||||
use Chill\CalendarBundle\Synchro\Connector\RemoteCalendarConnectorInterface;
|
use Chill\CalendarBundle\Synchro\Connector\RemoteCalendarConnectorInterface;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains method to get events (Calendar) from remote calendar.
|
* Contains method to get events (Calendar) from remote calendar.
|
||||||
@ -24,18 +29,45 @@ class RemoteCalendarProxyController
|
|||||||
{
|
{
|
||||||
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
|
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
|
||||||
|
|
||||||
|
private SerializerInterface $serializer;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(RemoteCalendarConnectorInterface $remoteCalendarConnector, SerializerInterface $serializer)
|
||||||
|
{
|
||||||
|
$this->remoteCalendarConnector = $remoteCalendarConnector;
|
||||||
|
$this->serializer = $serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("api/1.0/calendar/proxy/calendar/by-user/{id}/events")
|
||||||
|
*/
|
||||||
public function listEventForCalendar(User $user, Request $request): Response
|
public function listEventForCalendar(User $user, Request $request): Response
|
||||||
{
|
{
|
||||||
if ($request->query->has('startDate')) {
|
if ($request->query->has('startDate')) {
|
||||||
$startDate = DateTimeImmutable::createFromFormat('Y-m-dTHis', $request->query->get('startDate') . '000000');
|
$startDate = DateTimeImmutable::createFromFormat('Y-m-d', $request->query->get('startDate'));
|
||||||
|
if (false === $startDate) {
|
||||||
|
throw new BadRequestHttpException("startDate on bad format");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new BadRequestHttpException('startDate not provided');
|
throw new BadRequestHttpException('startDate not provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->query->has('endDate')) {
|
if ($request->query->has('endDate')) {
|
||||||
$startDate = DateTimeImmutable::createFromFormat('Y-m-dTHis', $request->query->get('endDate') . '000000');
|
$endDate = DateTimeImmutable::createFromFormat('Y-m-d', $request->query->get('endDate'));
|
||||||
|
if (false === $endDate) {
|
||||||
|
throw new BadRequestHttpException("endDate on bad format");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new BadRequestHttpException('endDate not provided');
|
throw new BadRequestHttpException('endDate not provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$events = $this->remoteCalendarConnector->listEventsForUser($user, $startDate, $endDate);
|
||||||
|
|
||||||
|
return new JsonResponse(
|
||||||
|
$this->serializer->serialize($events, 'json', ['groups' => ['read']]),
|
||||||
|
JsonResponse::HTTP_OK,
|
||||||
|
[],
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\CalendarBundle\Synchro\Connector\MSGraph;
|
namespace Chill\CalendarBundle\Synchro\Connector\MSGraph;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||||
|
use TheNetworg\OAuth2\Client\Provider\Azure;
|
||||||
use TheNetworg\OAuth2\Client\Token\AccessToken;
|
use TheNetworg\OAuth2\Client\Token\AccessToken;
|
||||||
|
|
||||||
class MSGraphTokenStorage
|
class MSGraphTokenStorage
|
||||||
@ -20,14 +21,32 @@ class MSGraphTokenStorage
|
|||||||
|
|
||||||
private SessionInterface $session;
|
private SessionInterface $session;
|
||||||
|
|
||||||
public function __construct(SessionInterface $session)
|
private Azure $azure;
|
||||||
|
|
||||||
|
public function __construct(Azure $azure, SessionInterface $session)
|
||||||
{
|
{
|
||||||
|
$this->azure = $azure;
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getToken(): AccessToken
|
public function getToken(): AccessToken
|
||||||
{
|
{
|
||||||
return $this->session->get(self::MS_GRAPH_ACCESS_TOKEN);
|
/** @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
|
public function hasToken(): bool
|
||||||
|
@ -29,12 +29,23 @@ class MachineHttpClient implements HttpClientInterface
|
|||||||
{
|
{
|
||||||
$options['headers'] = array_merge(
|
$options['headers'] = array_merge(
|
||||||
$options['headers'] ?? [],
|
$options['headers'] ?? [],
|
||||||
//['Content-Type' => 'application/json'],
|
|
||||||
$this->getAuthorizationHeaders($this->machineTokenStorage->getToken())
|
$this->getAuthorizationHeaders($this->machineTokenStorage->getToken())
|
||||||
);
|
);
|
||||||
$options['base_uri'] = 'https://graph.microsoft.com/v1.0/';
|
$options['base_uri'] = 'https://graph.microsoft.com/v1.0/';
|
||||||
|
|
||||||
dump($options);
|
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);
|
return $this->decoratedClient->request($method, $url, $options);
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,7 @@ class MachineTokenStorage
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
dump($this->accessToken);
|
|
||||||
|
|
||||||
return $this->accessToken;
|
return $this->accessToken;
|
||||||
|
|
||||||
//return unserialize($this->chillRedis->get(self::KEY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function storeToken(AccessToken $token): void
|
public function storeToken(AccessToken $token): void
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\CalendarBundle\Synchro\Connector\MSGraph;
|
||||||
|
|
||||||
|
use Chill\CalendarBundle\Synchro\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 MSGraphRemoteCalendarConnector $remoteCalendarConnector;
|
||||||
|
|
||||||
|
private LoggerInterface $logger;
|
||||||
|
|
||||||
|
public function __construct(MSGraphRemoteCalendarConnector $remoteCalendarConnector, LoggerInterface $logger)
|
||||||
|
{
|
||||||
|
$this->remoteCalendarConnector = $remoteCalendarConnector;
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCalendarId(User $user): ?string
|
||||||
|
{
|
||||||
|
if (null === $mskey = ($user->getAttributes()[self::METADATA_KEY] ?? null)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $msKey['defaultCalendarId'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\CalendarBundle\Synchro\Connector\MSGraph;
|
||||||
|
|
||||||
|
use Chill\CalendarBundle\Synchro\Model\RemoteEvent;
|
||||||
|
|
||||||
|
class RemoteEventConverter
|
||||||
|
{
|
||||||
|
public function convertToRemote(array $event): RemoteEvent
|
||||||
|
{
|
||||||
|
return new RemoteEvent(
|
||||||
|
$event['id'],
|
||||||
|
$event['subject'],
|
||||||
|
'',
|
||||||
|
\DateTimeImmutable::createFromFormat('Y-m-dTH:i:s.u', $event['start']['dateTime'], new \DateTimeZone('UTC')),
|
||||||
|
\DateTimeImmutable::createFromFormat('Y-m-dTH:i:s.u', $event['end']['dateTime'], new \DateTimeZone('UTC'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\CalendarBundle\Synchro\Connector\MSGraph;
|
||||||
|
|
||||||
|
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
|
||||||
|
|
||||||
|
class UserHttpClient
|
||||||
|
{
|
||||||
|
private HttpClientInterface $decoratedClient;
|
||||||
|
|
||||||
|
private MSGraphTokenStorage $tokenStorage;
|
||||||
|
|
||||||
|
use BearerAuthorizationTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HttpClientInterface $decoratedClient
|
||||||
|
*/
|
||||||
|
public function __construct(MSGraphTokenStorage $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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,8 @@ namespace Chill\CalendarBundle\Synchro\Connector;
|
|||||||
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MachineHttpClient;
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MachineHttpClient;
|
||||||
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MSGraphClient;
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MSGraphClient;
|
||||||
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MSGraphTokenStorage;
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MSGraphTokenStorage;
|
||||||
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\RemoteEventConverter;
|
||||||
|
use Chill\CalendarBundle\Synchro\Connector\MSGraph\UserHttpClient;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
@ -23,24 +25,28 @@ use function Amp\Iterator\toArray;
|
|||||||
|
|
||||||
class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||||
{
|
{
|
||||||
private MSGraphClient $client;
|
|
||||||
|
|
||||||
private MachineHttpClient $machineHttpClient;
|
private MachineHttpClient $machineHttpClient;
|
||||||
|
|
||||||
|
private UserHttpClient $userHttpClient;
|
||||||
|
|
||||||
private MSGraphTokenStorage $tokenStorage;
|
private MSGraphTokenStorage $tokenStorage;
|
||||||
|
|
||||||
private UrlGeneratorInterface $urlGenerator;
|
private UrlGeneratorInterface $urlGenerator;
|
||||||
|
|
||||||
|
private RemoteEventConverter $remoteEventConverter;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
MachineHttpClient $machineHttpClient,
|
MachineHttpClient $machineHttpClient,
|
||||||
MSGraphClient $client,
|
|
||||||
MSGraphTokenStorage $tokenStorage,
|
MSGraphTokenStorage $tokenStorage,
|
||||||
UrlGeneratorInterface $urlGenerator
|
RemoteEventConverter $remoteEventConverter,
|
||||||
|
UrlGeneratorInterface $urlGenerator,
|
||||||
|
UserHttpClient $userHttpClient
|
||||||
) {
|
) {
|
||||||
$this->client = $client;
|
|
||||||
$this->machineHttpClient = $machineHttpClient;
|
$this->machineHttpClient = $machineHttpClient;
|
||||||
|
$this->remoteEventConverter = $remoteEventConverter;
|
||||||
$this->tokenStorage = $tokenStorage;
|
$this->tokenStorage = $tokenStorage;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
$this->userHttpClient = $userHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMakeReadyResponse(string $returnPath): Response
|
public function getMakeReadyResponse(string $returnPath): Response
|
||||||
@ -56,20 +62,36 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
|||||||
|
|
||||||
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array
|
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array
|
||||||
{
|
{
|
||||||
return $this->client->listEventsForUserCalendar($user->getEmail(), $startDate, $endDate);
|
$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']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserByEmail(string $email): array
|
public function getUserByEmail(string $email): ?array
|
||||||
{
|
{
|
||||||
return $this->machineHttpClient->request('GET', 'users', [
|
$value = $this->machineHttpClient->request('GET', 'users', [
|
||||||
'query' => ['$filter' => "mail eq '${email}'"],
|
'query' => ['$filter' => "mail eq '${email}'"],
|
||||||
])->toArray()['value'];
|
])->toArray()['value'];
|
||||||
|
|
||||||
|
return $value[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDefaultUserCalendar(string $idOrUserPrincipalName): array
|
public function getDefaultUserCalendar(string $idOrUserPrincipalName): ?array
|
||||||
{
|
{
|
||||||
return $this->machineHttpClient->request('GET', "users/$idOrUserPrincipalName/calendars", [
|
$value = $this->machineHttpClient->request('GET', "users/$idOrUserPrincipalName/calendars", [
|
||||||
'query' => ['$filter' => 'isDefaultCalendar eq true'],
|
'query' => ['$filter' => 'isDefaultCalendar eq true'],
|
||||||
])->toArray()['value'];
|
])->toArray()['value'];
|
||||||
|
|
||||||
|
return $value[0] ?? null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,19 +12,36 @@ declare(strict_types=1);
|
|||||||
namespace Chill\CalendarBundle\Synchro\Model;
|
namespace Chill\CalendarBundle\Synchro\Model;
|
||||||
|
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
|
||||||
class RemoteEvent
|
class RemoteEvent
|
||||||
{
|
{
|
||||||
|
|
||||||
public string $description;
|
public string $description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Serializer\Groups({"read"})
|
||||||
|
*/
|
||||||
public DateTimeImmutable $endDate;
|
public DateTimeImmutable $endDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Serializer\Groups({"read"})
|
||||||
|
*/
|
||||||
|
public string $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Serializer\Groups({"read"})
|
||||||
|
*/
|
||||||
public DateTimeImmutable $startDate;
|
public DateTimeImmutable $startDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Serializer\Groups({"read"})
|
||||||
|
*/
|
||||||
public string $title;
|
public string $title;
|
||||||
|
|
||||||
public function __construct(string $title, string $description, DateTimeImmutable $startDate, DateTimeImmutable $endDate)
|
public function __construct(string $id, string $title, string $description, DateTimeImmutable $startDate, DateTimeImmutable $endDate)
|
||||||
{
|
{
|
||||||
|
$this->id = $id;
|
||||||
$this->title = $title;
|
$this->title = $title;
|
||||||
$this->description = $description;
|
$this->description = $description;
|
||||||
$this->startDate = $startDate;
|
$this->startDate = $startDate;
|
||||||
|
@ -43,9 +43,9 @@ class User implements AdvancedUserInterface
|
|||||||
/**
|
/**
|
||||||
* Array where SAML attributes's data are stored.
|
* Array where SAML attributes's data are stored.
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="json", nullable=true)
|
* @ORM\Column(type="json", nullable=false)
|
||||||
*/
|
*/
|
||||||
private array $attributes;
|
private array $attributes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=Location::class)
|
* @ORM\ManyToOne(targetEntity=Location::class)
|
||||||
|
@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\GroupCenter;
|
|||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\Query\ResultSetMapping;
|
||||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
@ -168,19 +169,29 @@ final class UserRepository implements ObjectRepository
|
|||||||
$rsm = new ResultSetMappingBuilder($this->entityManager);
|
$rsm = new ResultSetMappingBuilder($this->entityManager);
|
||||||
$rsm->addRootEntityFromClassMetadata(User::class, 'u');
|
$rsm->addRootEntityFromClassMetadata(User::class, 'u');
|
||||||
|
|
||||||
$sql = "SELECT ".$rsm->generateSelectClause()." FROM users u WHERE NOT attributes ? :key OR attributes IS NULL AND enabled IS TRUE";
|
$sql = "SELECT ".$rsm->generateSelectClause()." FROM users u WHERE NOT attributes ?? :key OR attributes IS NULL AND enabled IS TRUE";
|
||||||
|
|
||||||
if (null !== $limit) {
|
if (null !== $limit) {
|
||||||
$sql .= " LIMIT $limit";
|
$sql .= " LIMIT $limit";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $offset) {
|
if (null !== $offset) {
|
||||||
$sql .= " OFFET $offset";
|
$sql .= " OFFSET $offset";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->entityManager->createNativeQuery($sql, $rsm)->setParameter(':key', $key)->getResult();
|
return $this->entityManager->createNativeQuery($sql, $rsm)->setParameter(':key', $key)->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function countByNotHavingAttribute(string $key): int
|
||||||
|
{
|
||||||
|
$rsm = new ResultSetMapping();
|
||||||
|
$rsm->addScalarResult('count', 'count');
|
||||||
|
|
||||||
|
$sql = "SELECT count(*) FROM users u WHERE NOT attributes ?? :key OR attributes IS NULL AND enabled IS TRUE";
|
||||||
|
|
||||||
|
return $this->entityManager->createNativeQuery($sql, $rsm)->setParameter(':key', $key)->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
public function getClassName()
|
public function getClassName()
|
||||||
{
|
{
|
||||||
return User::class;
|
return User::class;
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Main;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20220506223243 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Force user attribute to be an array';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('UPDATE users SET attributes = \'{}\'::jsonb WHERE attributes IS NULL');
|
||||||
|
$this->addSql('ALTER TABLE users ALTER attributes SET NOT NULL');
|
||||||
|
$this->addSql('ALTER TABLE users ALTER attributes SET DEFAULT \'{}\'::jsonb');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE users ALTER attributes DROP NOT NULL');
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user