diff --git a/src/DataPersister/ProxyDataPersister.php b/src/DataPersister/ProxyDataPersister.php index 4e597cb255aa660fd56abd9199746cb31c93d3b1..07bcfdf5523541f512a3975b8f710ca09c006eb0 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 791f944fe33abf25982ac6c1de20a0ab61204280..a73602ad482fd4f629baf27e51814d85056099ec 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 65932072cf1f0af029602962f81fc31405484775..c3d12e15ab2eddb9bd02ed78fcb2a448dab8d01a 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 0000000000000000000000000000000000000000..2bc93fe5d849adf95bc663fc5956e614fe8c7fdd --- /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); +}