Ver código fonte

Merge branch 'hotfix/V8-7468-mettre-en-place-le-systme-de-con' into develop

Olivier Massot 5 meses atrás
pai
commit
84b0e51788

+ 4 - 1
src/Entity/Access/Access.php

@@ -63,6 +63,7 @@ use App\Entity\Traits\CreatedOnAndByTrait;
 use App\Filter\ApiPlatform\Person\FullNameFilter;
 use App\Filter\ApiPlatform\Utils\InFilter;
 use App\Repository\Access\AccessRepository;
+use App\State\Processor\Access\AccessProcessor;
 use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
@@ -80,7 +81,9 @@ use Symfony\Component\Serializer\Annotation\Groups;
  *     @see ~/config/api_platform/Access/access.yaml
  *     @see \App\Doctrine\Access\CurrentAccessExtension
  */
-#[ApiResource]
+#[ApiResource(
+    processor: AccessProcessor::class
+)]
 #[Auditable]
 #[ORM\Entity(repositoryClass: AccessRepository::class)]
 #[ApiFilter(filterClass: BooleanFilter::class, properties: ['person.isPhysical'])]

+ 3 - 3
src/Service/MercureHub.php

@@ -13,10 +13,10 @@ use Symfony\Component\Serializer\SerializerInterface;
 /**
  * Sends private and encrypted mercure updates to the target users.
  *
- * Updates inform of modifications on entities : updates, creations, deletions.
+ * Updates inform of modifications on entities: updates, creations, deletions.
  *
  * The update topic is the id of the recipient user.
- * The content is a json containing the iri of the entity, the operation type, and the current data of this entity
+ * The content is a JSON containing the iri of the entity, the operation type, and the current data of this entity
  */
 class MercureHub
 {
@@ -42,7 +42,7 @@ class MercureHub
     }
 
     /**
-     * Send an update to the.
+     * Send an update to the client.
      */
     public function publish(int $accessId, mixed $entity, string $operationType = self::UPDATE): void
     {

+ 48 - 0
src/Service/OnChange/Access/OnAccessChange.php

@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Service\OnChange\Access;
+
+use App\Entity\Access\Access;
+use App\Service\Access\AccessProfileCreator;
+use App\Service\MercureHub;
+use App\Service\OnChange\OnChangeContext;
+use App\Service\OnChange\OnChangeDefault;
+use Symfony\Bundle\SecurityBundle\Security;
+use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
+
+/**
+ * Classe OnAccessChange qui comporte toutes les opérations automatiques se produisant lors de l'évolution d'un Access.
+ */
+class OnAccessChange extends OnChangeDefault
+{
+    public function __construct(
+        private Security $security,
+        private AccessProfileCreator $accessProfileCreator,
+        private MercureHub $mercureHub,
+    ) {
+    }
+
+    public function onChange(mixed $access, OnChangeContext $context): void
+    {
+        $this->publishNewProfile($access);
+    }
+
+    /**
+     * Publie via mercure le nouveau profil de l'access.
+     *
+     * @throws \Exception
+     */
+    public function publishNewProfile(Access $access): void
+    {
+        $token = $this->security->getToken();
+
+        /** @var ?Access $originalAccess */
+        $originalAccess = $token instanceof SwitchUserToken ? $token->getOriginalToken()->getUser() : null;
+
+        $profile = $this->accessProfileCreator->getAccessProfile($access, $originalAccess);
+
+        $this->mercureHub->publishUpdate($access->getId(), $profile);
+    }
+}

+ 22 - 0
src/State/Processor/Access/AccessProcessor.php

@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\State\Processor\Access;
+
+use App\Service\OnChange\Access\OnAccessChange;
+use App\State\Processor\EntityProcessor;
+use JetBrains\PhpStorm\Pure;
+
+/**
+ * Classe AccessProcessor qui est un custom dataPersister gérant la resource Access.
+ */
+class AccessProcessor extends EntityProcessor
+{
+    #[Pure]
+    public function __construct(
+        OnAccessChange $onChange,
+    ) {
+        parent::__construct($onChange);
+    }
+}

+ 0 - 3
src/State/Processor/Core/FileProcessor.php

@@ -8,9 +8,6 @@ use App\Service\OnChange\Core\OnFileChange;
 use App\State\Processor\EntityProcessor;
 use JetBrains\PhpStorm\Pure;
 
-/**
- * Classe OrganizationProcessor qui est un custom dataPersister gérant la resource Organization.
- */
 class FileProcessor extends EntityProcessor
 {
     #[Pure]

+ 126 - 0
tests/Unit/Service/OnChange/Access/OnAccessChangeTest.php

@@ -0,0 +1,126 @@
+<?php
+
+/** @noinspection PhpUnhandledExceptionInspection */
+
+namespace App\Tests\Unit\Service\OnChange\Access;
+
+use App\ApiResources\Profile\AccessProfile;
+use App\Entity\Access\Access;
+use App\Service\Access\AccessProfileCreator;
+use App\Service\MercureHub;
+use App\Service\OnChange\Access\OnAccessChange;
+use App\Service\OnChange\OnChangeContext;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Symfony\Bundle\SecurityBundle\Security;
+use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+class OnAccessChangeTest extends TestCase
+{
+    private Security|MockObject $security;
+    private AccessProfileCreator|MockObject $accessProfileCreator;
+    private MercureHub|MockObject $mercureHub;
+
+    public function setUp(): void
+    {
+        $this->security = $this->getMockBuilder(Security::class)->disableOriginalConstructor()->getMock();
+        $this->accessProfileCreator = $this->getMockBuilder(AccessProfileCreator::class)->disableOriginalConstructor()->getMock();
+        $this->mercureHub = $this->getMockBuilder(MercureHub::class)->disableOriginalConstructor()->getMock();
+    }
+
+    /**
+     * @see OnAccessChange::onChange()
+     */
+    public function testOnChange(): void
+    {
+        $onAccessChange = $this->getMockBuilder(OnAccessChange::class)
+            ->setConstructorArgs([$this->security, $this->accessProfileCreator, $this->mercureHub])
+            ->setMethodsExcept(['onChange'])
+            ->getMock();
+
+        $access = $this->getMockBuilder(Access::class)->getMock();
+        $context = $this->getMockBuilder(OnChangeContext::class)->disableOriginalConstructor()->getMock();
+
+        // The onChange method should call publishNewProfile
+        $onAccessChange->expects($this->once())
+            ->method('publishNewProfile')
+            ->with($access);
+
+        $onAccessChange->onChange($access, $context);
+    }
+
+    /**
+     * @see OnAccessChange::publishNewProfile()
+     */
+    public function testPublishNewProfileWithRegularToken(): void
+    {
+        $onAccessChange = $this->getMockBuilder(OnAccessChange::class)
+            ->setConstructorArgs([$this->security, $this->accessProfileCreator, $this->mercureHub])
+            ->setMethodsExcept(['publishNewProfile'])
+            ->getMock();
+
+        $access = $this->getMockBuilder(Access::class)->getMock();
+        $access->method('getId')->willReturn(123);
+
+        $token = $this->getMockBuilder(TokenInterface::class)->getMock();
+
+        $this->security->expects($this->once())
+            ->method('getToken')
+            ->willReturn($token);
+
+        $accessProfile = $this->getMockBuilder(AccessProfile::class)->disableOriginalConstructor()->getMock();
+
+        $this->accessProfileCreator->expects($this->once())
+            ->method('getAccessProfile')
+            ->with($access, null)
+            ->willReturn($accessProfile);
+
+        $this->mercureHub->expects($this->once())
+            ->method('publishUpdate')
+            ->with(123, $accessProfile);
+
+        $onAccessChange->publishNewProfile($access);
+    }
+
+    /**
+     * @see OnAccessChange::publishNewProfile()
+     */
+    public function testPublishNewProfileWithSwitchUserToken(): void
+    {
+        $onAccessChange = $this->getMockBuilder(OnAccessChange::class)
+            ->setConstructorArgs([$this->security, $this->accessProfileCreator, $this->mercureHub])
+            ->setMethodsExcept(['publishNewProfile'])
+            ->getMock();
+
+        $access = $this->getMockBuilder(Access::class)->getMock();
+        $access->method('getId')->willReturn(123);
+
+        $originalAccess = $this->getMockBuilder(Access::class)->getMock();
+
+        $originalToken = $this->getMockBuilder(TokenInterface::class)->getMock();
+        $originalToken->method('getUser')->willReturn($originalAccess);
+
+        $switchUserToken = $this->getMockBuilder(SwitchUserToken::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $switchUserToken->method('getOriginalToken')->willReturn($originalToken);
+
+        $this->security->expects($this->once())
+            ->method('getToken')
+            ->willReturn($switchUserToken);
+
+        $accessProfile = $this->getMockBuilder(AccessProfile::class)->disableOriginalConstructor()->getMock();
+
+        $this->accessProfileCreator->expects($this->once())
+            ->method('getAccessProfile')
+            ->with($access, $originalAccess)
+            ->willReturn($accessProfile);
+
+        $this->mercureHub->expects($this->once())
+            ->method('publishUpdate')
+            ->with(123, $accessProfile);
+
+        $onAccessChange->publishNewProfile($access);
+    }
+}