From 2d53f54d96ca09e81df3f265da336d4d15a12f49 Mon Sep 17 00:00:00 2001
From: Tobias Gross-Vogt <tgros@tugraz.at>
Date: Mon, 19 Sep 2022 10:22:34 +0200
Subject: [PATCH] added proxy data event subscriber base class to make event
 subscriber implementation easier

---
 src/DataPersister/ProxyDataPersister.php      | 17 +++++-
 src/Entity/ProxyData.php                      |  4 +-
 src/Event/ProxyDataEvent.php                  | 20 +++++++
 .../ProxyDataEventSubscriber.php              | 54 +++++++++++++++++++
 4 files changed, 91 insertions(+), 4 deletions(-)
 create mode 100644 src/EventSubscriber/ProxyDataEventSubscriber.php

diff --git a/src/DataPersister/ProxyDataPersister.php b/src/DataPersister/ProxyDataPersister.php
index 4e597cb..07bcfdf 100644
--- a/src/DataPersister/ProxyDataPersister.php
+++ b/src/DataPersister/ProxyDataPersister.php
@@ -5,10 +5,12 @@ declare(strict_types=1);
 namespace Dbp\Relay\ProxyBundle\DataPersister;
 
 use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
+use Dbp\Relay\CoreBundle\Helpers\Tools;
 use Dbp\Relay\ProxyBundle\Entity\ProxyData;
 use Dbp\Relay\ProxyBundle\Event\ProxyDataEvent;
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\HttpFoundation\Exception\BadRequestException;
 
 class ProxyDataPersister extends AbstractController implements ContextAwareDataPersisterInterface
 {
@@ -32,8 +34,19 @@ class ProxyDataPersister extends AbstractController implements ContextAwareDataP
     {
         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
 
-        $proxyDataEvent = new ProxyDataEvent($data);
-        $this->eventDispatcher->dispatch($proxyDataEvent, ProxyDataEvent::NAME.$data->getNamespace());
+        if (Tools::isNullOrEmpty($data->getNamespace())) {
+            throw new BadRequestException('parameter namespace must not be null nor empty');
+        } elseif (Tools::isNullOrEmpty($data->getFunctionName())) {
+            throw new BadRequestException('parameter functionName must not be null nor empty');
+        } else {
+            $proxyDataEvent = new ProxyDataEvent($data);
+            $this->eventDispatcher->dispatch($proxyDataEvent, ProxyDataEvent::NAME.$data->getNamespace());
+
+            if ($proxyDataEvent->wasHandled() === false) {
+                throw new BadRequestException(sprintf('unknown namespace "%s"', $data->getNamespace()));
+            }
+        }
+
         $data->setIdentifier($data->getNamespace().'::'.$data->getFunctionName());
 
         return $data;
diff --git a/src/Entity/ProxyData.php b/src/Entity/ProxyData.php
index 791f944..a73602a 100644
--- a/src/Entity/ProxyData.php
+++ b/src/Entity/ProxyData.php
@@ -147,7 +147,7 @@ class ProxyData
         $this->errors = $errors;
     }
 
-    public function getFunctionName(): string
+    public function getFunctionName(): ?string
     {
         return $this->functionName;
     }
@@ -157,7 +157,7 @@ class ProxyData
         $this->functionName = $functionName;
     }
 
-    public function getNamespace(): string
+    public function getNamespace(): ?string
     {
         return $this->namespace;
     }
diff --git a/src/Event/ProxyDataEvent.php b/src/Event/ProxyDataEvent.php
index 6593207..c3d12e1 100644
--- a/src/Event/ProxyDataEvent.php
+++ b/src/Event/ProxyDataEvent.php
@@ -14,13 +14,33 @@ class ProxyDataEvent extends Event
     /** @var ProxyData */
     private $proxyData;
 
+    /** @var bool */
+    private $wasHandled;
+
     public function __construct(ProxyData $proxyData)
     {
         $this->proxyData = $proxyData;
+        $this->wasHandled = false;
     }
 
     public function getProxyData(): ProxyData
     {
         return $this->proxyData;
     }
+
+    /**
+     * Indicate, that the event was handled, e.g. there was an event subscriber for the requested proxy data namespace.
+     */
+    public function setHandled(): void
+    {
+        $this->wasHandled = true;
+    }
+
+    /**
+     * True, if the event was handled, e.g. there was an event subscriber for the requested proxy data namespace, false otherwise.
+     */
+    public function wasHandled(): bool
+    {
+        return $this->wasHandled;
+    }
 }
diff --git a/src/EventSubscriber/ProxyDataEventSubscriber.php b/src/EventSubscriber/ProxyDataEventSubscriber.php
new file mode 100644
index 0000000..2bc93fe
--- /dev/null
+++ b/src/EventSubscriber/ProxyDataEventSubscriber.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\ProxyBundle\EventSubscriber;
+
+use Dbp\Relay\ProxyBundle\Event\ProxyDataEvent;
+use Exception;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Exception\BadRequestException;
+
+abstract class ProxyDataEventSubscriber implements EventSubscriberInterface
+{
+    protected const NAMESPACE = '';
+
+    public static function getSubscribedEvents(): array
+    {
+        return [
+            ProxyDataEvent::NAME.static::NAMESPACE => 'onProxyDataEvent',
+        ];
+    }
+
+    /**
+     * @throws BadRequestException
+     */
+    public function onProxyDataEvent(ProxyDataEvent $event): void
+    {
+        $event->setHandled();
+        $proxyData = $event->getProxyData();
+        $functionName = $proxyData->getFunctionName();
+        $arguments = $proxyData->getArguments();
+        $returnValue = null;
+
+        if ($this->isFunctionDefined($functionName) === false) {
+            throw new BadRequestException(sprintf('unknown function "%s" under namespace "%s"', $functionName, static::NAMESPACE));
+        } elseif ($this->areAllRequiredArgumentsDefined($arguments) === false) {
+            throw new BadRequestException(sprintf('incomplete argument list for function "%s" under namespace "%s"', $functionName, static::NAMESPACE));
+        }
+
+        try {
+            $returnValue = $this->callFunction($functionName, $arguments);
+        } catch (Exception $exception) {
+            $proxyData->setErrorsFromException($exception);
+        }
+
+        $proxyData->setData($returnValue);
+    }
+
+    abstract protected function isFunctionDefined(string $functionName): bool;
+
+    abstract protected function areAllRequiredArgumentsDefined(array $arguments): bool;
+
+    abstract protected function callFunction(string $functionName, array $arguments);
+}
-- 
GitLab