Explorar o código

Implements HelloAsso event forms retrieval

Adds functionality to retrieve HelloAsso event forms, replacing the previous organization form retrieval.
It introduces a new `EventForm` API resource and provider, and refactors the HelloAsso service to fetch and map event form data from the HelloAsso API, including fetching a form by event ID.
The changes also include updating the product configuration, adding HelloAsso slug and URL to the Event entity to link event with helloasso form.
Olivier Massot hai 11 meses
pai
achega
f804d9d0e2

+ 1 - 1
config/opentalent/products.yaml

@@ -38,7 +38,7 @@ parameters:
           - ConnectionRequest
           - UnlinkRequest
           - HelloAssoProfile
-          - OrganizationForm
+          - EventForm
         roles:
           - ROLE_IMPORT
           - ROLE_TAGG

+ 44 - 13
src/ApiResources/HelloAsso/EventForm.php

@@ -8,68 +8,99 @@ use ApiPlatform\Metadata\ApiProperty;
 use ApiPlatform\Metadata\ApiResource;
 use ApiPlatform\Metadata\Get;
 use ApiPlatform\Metadata\GetCollection;
+use ApiPlatform\Metadata\Link;
 use App\State\Provider\HelloAsso\AuthUrlProvider;
 use App\State\Provider\HelloAsso\HelloAssoProfileProvider;
-use App\State\Provider\HelloAsso\OrganizationFormProvider;
+use App\State\Provider\HelloAsso\EventFormProvider;
 
 /**
  * Retourne les formulaires d'une organisation.
  */
 #[ApiResource(
     operations: [
+        new GetCollection(
+            uriTemplate: '/helloasso/forms',
+        ),
         new Get(
-            uriTemplate: '/helloasso/form/{eventId}',
+            uriTemplate: '/helloasso/form/{slug}',
+        ),
+        new Get(
+            uriTemplate: '/helloasso/form/by-event/{eventId}',
+            uriVariables: [
+                'eventId' => new Link(
+                    fromProperty: 'eventId',
+                    identifiers: ['eventId']
+                )
+            ]
         )
     ],
-    provider: OrganizationFormProvider::class,
+    provider: EventFormProvider::class,
     security: 'is_granted("ROLE_ORGANIZATION")'
 )]
-class OrganizationForm
+class EventForm
 {
     /**
      * Slug du formulaire
      */
-    private string $slug;
+    #[ApiProperty(identifier: true)]
+    private ?string $slug = null;
+
+    /**
+     * Id de l'Event auquel le formulaire est associé
+     * @var int | null
+     */
+    private ?int $eventId = null;
 
     /**
      * Titre du formulaire
      * @var bool
      */
-    private string $title;
+    private ?string $title = null;
 
     /**
      * Url du formulaire
      */
-    private string $widgetUrl;
+    private ?string $widgetUrl = null;
 
-    public function getSlug(): string
+    public function getSlug(): ?string
     {
         return $this->slug;
     }
 
-    public function setSlug(string $slug): self
+    public function setSlug(?string $slug): self
     {
         $this->slug = $slug;
         return $this;
     }
 
-    public function getTitle(): string
+    public function getEventId(): ?int
+    {
+        return $this->eventId;
+    }
+
+    public function setEventId(?int $eventId): self
+    {
+        $this->eventId = $eventId;
+        return $this;
+    }
+
+    public function getTitle(): ?string
     {
         return $this->title;
     }
 
-    public function setTitle(string $title): self
+    public function setTitle(?string $title): self
     {
         $this->title = $title;
         return $this;
     }
 
-    public function getWidgetUrl(): string
+    public function getWidgetUrl(): ?string
     {
         return $this->widgetUrl;
     }
 
-    public function setWidgetUrl(string $widgetUrl): self
+    public function setWidgetUrl(?string $widgetUrl): self
     {
         $this->widgetUrl = $widgetUrl;
         return $this;

+ 28 - 0
src/Entity/Booking/Event.php

@@ -117,6 +117,12 @@ class Event extends AbstractBooking
     #[Assert\Positive]
     protected ?float $priceMaxi = null;
 
+    #[ORM\Column(length: 255, nullable: true)]
+    private ?string $helloAssoSlug = null;
+
+    #[ORM\Column(length: 255, nullable: true)]
+    private ?string $helloAssoPublicUrl = null;
+
     public function __construct()
     {
         $this->eventRecur = new ArrayCollection();
@@ -474,4 +480,26 @@ class Event extends AbstractBooking
         $this->priceMaxi = $priceMaxi;
         return $this;
     }
+
+    public function getHelloAssoSlug(): ?string
+    {
+        return $this->helloAssoSlug;
+    }
+
+    public function setHelloAssoSlug(?string $helloAssoSlug): self
+    {
+        $this->helloAssoSlug = $helloAssoSlug;
+        return $this;
+    }
+
+    public function getHelloAssoPublicUrl(): ?string
+    {
+        return $this->helloAssoPublicUrl;
+    }
+
+    public function setHelloAssoPublicUrl(?string $helloAssoPublicUrl): self
+    {
+        $this->helloAssoPublicUrl = $helloAssoPublicUrl;
+        return $this;
+    }
 }

+ 102 - 30
src/Service/HelloAsso/HelloAssoService.php

@@ -6,9 +6,12 @@ namespace App\Service\HelloAsso;
 
 use App\ApiResources\HelloAsso\AuthUrl;
 use App\ApiResources\HelloAsso\HelloAssoProfile;
-use App\ApiResources\HelloAsso\OrganizationForm;
+use App\ApiResources\HelloAsso\EventForm;
+use App\Entity\Booking\Event;
 use App\Entity\HelloAsso\HelloAsso;
 use App\Entity\Organization\Organization;
+use App\Repository\Booking\EventRepository;
+use App\Repository\Organization\OrganizationRepository;
 use App\Service\Rest\ApiRequestService;
 use App\Service\Security\OAuthPkceGenerator;
 use App\Service\Utils\DatesUtils;
@@ -17,7 +20,9 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\HttpKernel\Exception\HttpException;
+use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
 use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Symfony\Contracts\HttpClient\ResponseInterface;
 
 /**
  * Service de connexion à HelloAsso.
@@ -29,6 +34,8 @@ class HelloAssoService extends ApiRequestService
 {
     public function __construct(
         HttpClientInterface $client,
+        private readonly OrganizationRepository $organizationRepository,
+        private readonly EventRepository $eventRepository,
         private readonly string $baseUrl,
         private readonly string $publicAppBaseUrl,
         private readonly string $helloAssoApiBaseUrl,
@@ -66,7 +73,7 @@ class HelloAssoService extends ApiRequestService
      */
     public function getAuthUrl(int $organizationId): AuthUrl
     {
-        $organization = $this->entityManager->getRepository(Organization::class)->find($organizationId);
+        $organization = $this->organizationRepository->find($organizationId);
 
         if (!$organization) {
             throw new \RuntimeException('Organization not found');
@@ -114,7 +121,7 @@ class HelloAssoService extends ApiRequestService
      */
     public function connect(int $organizationId, string $authorizationCode): HelloAsso
     {
-        $organization = $this->entityManager->getRepository(Organization::class)->find($organizationId);
+        $organization = $this->organizationRepository->find($organizationId);
         if (!$organization) {
             throw new \RuntimeException('Organization not found');
         }
@@ -154,7 +161,7 @@ class HelloAssoService extends ApiRequestService
      */
     public function makeHelloAssoProfile(int $organizationId): HelloAssoProfile
     {
-        $organization = $this->entityManager->getRepository(Organization::class)->find($organizationId);
+        $organization = $this->organizationRepository->find($organizationId);
         if (!$organization) {
             throw new \RuntimeException('Organization not found');
         }
@@ -175,7 +182,7 @@ class HelloAssoService extends ApiRequestService
 
     public function unlinkHelloAssoAccount(int $organizationId): void
     {
-        $organization = $this->entityManager->getRepository(Organization::class)->find($organizationId);
+        $organization = $this->organizationRepository->find($organizationId);
         if (!$organization) {
             throw new \RuntimeException('Organization not found');
         }
@@ -195,28 +202,20 @@ class HelloAssoService extends ApiRequestService
         $this->entityManager->flush();
     }
 
-    public function getHelloAssoForms(int $organizationId): array
-    {
-        $organization = $this->entityManager->getRepository(Organization::class)->find($organizationId);
-        if (!$organization) {
-            throw new \RuntimeException('Organization not found');
-        }
-
-        $helloAssoEntity = $organization->getHelloAsso();
-        if (!$helloAssoEntity) {
-            throw new \RuntimeException('HelloAsso entity not found');
-        }
+    public function getResource(HelloAsso $helloAssoEntity, array $routeParts): array {
         if (!$helloAssoEntity->getOrganizationSlug() || !$helloAssoEntity->getToken()) {
             throw new \RuntimeException('HelloAsso entity incomplete');
         }
 
         $helloAssoEntity = $this->refreshTokenIfNeeded($helloAssoEntity);
 
+        $url = UrlBuilder::concat(
+            $this->helloAssoApiBaseUrl,
+            array_merge(['/v5'], $routeParts),
+        );
+
         $response = $this->get(
-            UrlBuilder::concat(
-                $this->helloAssoApiBaseUrl,
-                ['/v5/organizations', $helloAssoEntity->getOrganizationSlug(), '/forms']
-            ),
+            $url,
             [],
             ['headers' =>
                 [
@@ -227,25 +226,98 @@ class HelloAssoService extends ApiRequestService
         );
 
         if ($response->getStatusCode() !== 200) {
-            throw new HttpException(500, 'Failed to fetch access token: '.$response->getContent(false));
+            throw new HttpException(
+                500,
+                'Failed to fetch resource: ['.$response->getStatusCode().'] '.$response->getContent(false)
+            );
         }
 
-        $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR)['data'];
+        return json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR);
+    }
 
-        $forms = [];
+    public function getHelloAssoEventForms(int $organizationId): array
+    {
+        $helloAssoEntity = $this->getHelloAssoEntityFor($organizationId);
+
+        $data = $this->getResource(
+            $helloAssoEntity,
+            ['organizations', $helloAssoEntity->getOrganizationSlug(), 'forms']
+        );
 
-        foreach ($data as $formData) {
-            $form = new OrganizationForm();
-            $form->setSlug($formData['formSlug']);
-            $form->setTitle($formData['title']);
-            $form->setUrl($formData['url']);
+        $forms = [];
 
-            $forms[] = $form;
+        foreach ($data['data'] as $formData) {
+            $forms[] = $this->makeHelloAssoEventForm($formData);
         }
 
         return $forms;
     }
 
+    public function getHelloAssoEventForm(int $organizationId, string $formSlug): EventForm
+    {
+        $helloAssoEntity = $this->getHelloAssoEntityFor($organizationId);
+
+        if (!$helloAssoEntity->getOrganizationSlug() || !$helloAssoEntity->getToken()) {
+            throw new \RuntimeException('HelloAsso entity incomplete');
+        }
+
+        $formType = 'Event';
+
+        $data = $this->getResource(
+            $helloAssoEntity,
+            ['organizations', $helloAssoEntity->getOrganizationSlug(), 'forms', $formType, $formSlug, 'public']
+        );
+
+        return $this->makeHelloAssoEventForm($data);
+    }
+
+    public function getHelloAssoEventFormByEventId(int $organizationId, int $eventId): EventForm
+    {
+        $helloAssoEntity = $this->getHelloAssoEntityFor($organizationId);
+
+        if (!$helloAssoEntity->getOrganizationSlug() || !$helloAssoEntity->getToken()) {
+            throw new \RuntimeException('HelloAsso entity incomplete');
+        }
+
+        $event = $this->eventRepository->find($eventId);
+        if (!$event) {
+            throw new \RuntimeException('Event not found');
+        }
+
+        $helloAssoFormSlug = $event->getHelloAssoSlug();
+        if (!$helloAssoFormSlug) {
+            throw new \RuntimeException('HelloAsso form slug not found');
+        }
+
+        return $this->getHelloAssoEventForm($organizationId, $helloAssoFormSlug);
+    }
+
+    protected function getHelloAssoEntityFor(int $organizationId): HelloAsso
+    {
+        $organization = $this->organizationRepository->find($organizationId);
+        if (!$organization) {
+            throw new \RuntimeException('Organization not found');
+        }
+        $helloAssoEntity = $organization->getHelloAsso();
+        if (!$helloAssoEntity) {
+            throw new \RuntimeException('HelloAsso entity not found');
+        }
+        return $helloAssoEntity;
+    }
+
+    /**
+     * Construit un objet EventForm à partir des données retournées par l'api HelloAsso.
+     * @param array $formData
+     * @return EventForm
+     */
+    protected function makeHelloAssoEventForm(array $formData): EventForm {
+        $form = new EventForm();
+        $form->setSlug($formData['formSlug']);
+        $form->setTitle($formData['title']);
+        $form->setWidgetUrl($formData['widgetFullUrl']);
+
+        return $form;
+    }
 
     /**
      * Génère l'URL de rappel pour les callbacks suite à l'authentification HelloAsso
@@ -351,7 +423,7 @@ class HelloAssoService extends ApiRequestService
             return $helloAssoEntity;
         }
 
-        return $this->refreshToken($helloAssoEntity);
+        return $this->refreshTokens($helloAssoEntity);
     }
 
     public function refreshTokens(HelloAsso $helloAssoEntity): HelloAsso {

+ 5 - 3
src/State/Provider/HelloAsso/EventFormProvider.php

@@ -19,7 +19,7 @@ use Symfony\Component\HttpFoundation\Response;
 /**
  * Provider pour la ressource HelloAssoProfile.
  */
-final class OrganizationFormProvider implements ProviderInterface
+final class EventFormProvider implements ProviderInterface
 {
     public function __construct(
         private HelloAssoService $helloAssoService,
@@ -41,11 +41,13 @@ final class OrganizationFormProvider implements ProviderInterface
         $organizationId = $access->getOrganization()->getId();
 
         if ($operation instanceof GetCollection) {
-            $forms = $this->helloAssoService->getHelloAssoForms($organizationId);
+            $forms = $this->helloAssoService->getHelloAssoEventForms($organizationId);
 
             return new ArrayCollection($forms);
+        } else if (isset($uriVariables['eventId'])) {
+            return $this->helloAssoService->getHelloAssoEventFormByEventId($organizationId, $uriVariables['eventId']);
         } else {
-            $form = $this->helloAssoService->getHelloAssoEventForm($organizationId, $uriVariables['id']);
+            return $this->helloAssoService->getHelloAssoEventForm($organizationId, $uriVariables['slug']);
         }
     }
 }