Skip to content

Commit

Permalink
Request events
Browse files Browse the repository at this point in the history
Co-authored-by: ArnoudThibaut <thibaut.arnoud@gmail.com>
  • Loading branch information
alanpoulain and ArnoudThibaut committed Apr 7, 2019
1 parent d86f956 commit 6c8b29f
Show file tree
Hide file tree
Showing 69 changed files with 2,023 additions and 497 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"doctrine/inflector": "^1.0",
"psr/cache": "^1.0",
"psr/container": "^1.0",
"symfony/event-dispatcher": "^3.4 || ^4.0",
"symfony/http-foundation": "^3.4 || ^4.0",
"symfony/http-kernel": "^3.4 || ^4.0",
"symfony/property-access": "^3.4 || ^4.0",
Expand Down Expand Up @@ -61,7 +62,6 @@
"symfony/dependency-injection": "^3.4 || ^4.0",
"symfony/doctrine-bridge": "^3.4 || ^4.0",
"symfony/dom-crawler": "^3.4 || ^4.0",
"symfony/event-dispatcher": "^3.4 || ^4.0",
"symfony/expression-language": "^3.4 || ^4.0",
"symfony/finder": "^3.4 || ^4.0",
"symfony/form": "^3.4 || ^4.0",
Expand Down
38 changes: 35 additions & 3 deletions src/Bridge/FosUser/EventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\Core\Bridge\FosUser;

use ApiPlatform\Core\Event\EventInterface;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Model\UserManagerInterface;
Expand All @@ -35,22 +36,53 @@ public function __construct(UserManagerInterface $userManager)

/**
* Persists, updates or delete data return by the controller if applicable.
*
* @deprecated since version 2.5, to bo removed in 3.0
*/
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$request = $event->getRequest();
@trigger_error(sprintf('The method %s() is deprecated since 2.5 and will be removed in 3.0.', __METHOD__), E_USER_DEPRECATED);

$this->handleEvent($event);
}

/**
* Persists, updates or delete data return by the controller if applicable.
*/
public function handleEvent(/* EventInterface */ $event)
{
if ($event instanceof EventInterface) {
$request = $event->getContext()['request'];
} elseif ($event instanceof GetResponseForControllerResultEvent) {
@trigger_error(sprintf('Passing an instance of "%s" as argument of "%s" is deprecated since 2.5 and will not be possible anymore in 3.0. Pass an instance of "%s" instead.', GetResponseForControllerResultEvent::class, __METHOD__, EventInterface::class), E_USER_DEPRECATED);

$request = $event->getRequest();
} else {
return;
}

if (!RequestAttributesExtractor::extractAttributes($request)) {
return;
}

$user = $event->getControllerResult();
if ($event instanceof EventInterface) {
$user = $event->getData();
} elseif ($event instanceof GetResponseForControllerResultEvent) {
$user = $event->getControllerResult();
} else {
return;
}
if (!$user instanceof UserInterface || $request->isMethodSafe(false)) {
return;
}

if ('DELETE' === $request->getMethod()) {
$this->userManager->deleteUser($user);
$event->setControllerResult(null);
if ($event instanceof EventInterface) {
$event->setData(null);
} elseif ($event instanceof GetResponseForControllerResultEvent) {
$event->setControllerResult(null);
}
} else {
$this->userManager->updateUser($user);
}
Expand Down
23 changes: 22 additions & 1 deletion src/Bridge/Symfony/Bundle/EventListener/SwaggerUiListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,37 @@

namespace ApiPlatform\Core\Bridge\Symfony\Bundle\EventListener;

use ApiPlatform\Core\Event\EventInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

final class SwaggerUiListener
{
/**
* Sets SwaggerUiAction as controller if the requested format is HTML.
*
* @deprecated since version 2.5, to be removed in 3.0.
*/
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
@trigger_error(sprintf('The method %s() is deprecated since 2.5 and will be removed in 3.0.', __METHOD__), E_USER_DEPRECATED);

$this->handleEvent($event);
}

/**
* Sets SwaggerUiAction as controller if the requested format is HTML.
*/
public function handleEvent(/*EventInterface */$event)
{
if ($event instanceof EventInterface) {
$request = $event->getContext()['request'];
} elseif ($event instanceof GetResponseEvent) {
@trigger_error(sprintf('Passing an instance of "%s" as argument of "%s" is deprecated since 2.5 and will not be possible anymore in 3.0. Pass an instance of "%s" instead.', GetResponseEvent::class, __METHOD__, EventInterface::class), E_USER_DEPRECATED);

$request = $event->getRequest();
} else {
return;
}
if (
'html' !== $request->getRequestFormat('') ||
(!$request->attributes->has('_api_resource_class') && !$request->attributes->has('_api_respond'))
Expand Down
136 changes: 136 additions & 0 deletions src/Bridge/Symfony/Bundle/EventSubscriber/EventDispatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Bridge\Symfony\Bundle\EventSubscriber;

use ApiPlatform\Core\Event\DeserializeEvent;
use ApiPlatform\Core\Event\Event as ApiPlatformEvent;
use ApiPlatform\Core\Event\FormatAddEvent;
use ApiPlatform\Core\Event\PostDeserializeEvent;
use ApiPlatform\Core\Event\PostReadEvent;
use ApiPlatform\Core\Event\PostRespondEvent;
use ApiPlatform\Core\Event\PostSerializeEvent;
use ApiPlatform\Core\Event\PostValidateEvent;
use ApiPlatform\Core\Event\PostWriteEvent;
use ApiPlatform\Core\Event\PreDeserializeEvent;
use ApiPlatform\Core\Event\PreReadEvent;
use ApiPlatform\Core\Event\PreRespondEvent;
use ApiPlatform\Core\Event\PreSerializeEvent;
use ApiPlatform\Core\Event\PreValidateEvent;
use ApiPlatform\Core\Event\PreWriteEvent;
use ApiPlatform\Core\Event\QueryParameterValidateEvent;
use ApiPlatform\Core\Event\ReadEvent;
use ApiPlatform\Core\Event\RespondEvent;
use ApiPlatform\Core\Event\SerializeEvent;
use ApiPlatform\Core\Event\ValidateEvent;
use ApiPlatform\Core\Event\ValidationExceptionEvent;
use ApiPlatform\Core\Event\WriteEvent;
use ApiPlatform\Core\Events;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
* @internal
*
* @author Alan Poulain <contact@alanpoulain.eu>
*/
final class EventDispatcher implements EventSubscriberInterface
{
private const INTERNAL_EVENTS_CONFIGURATION = [
KernelEvents::REQUEST => [
QueryParameterValidateEvent::class => Events::QUERY_PARAMETER_VALIDATE,
FormatAddEvent::class => Events::FORMAT_ADD,

PreReadEvent::class => Events::PRE_READ,
ReadEvent::class => Events::READ,
PostReadEvent::class => Events::POST_READ,

PreDeserializeEvent::class => Events::PRE_DESERIALIZE,
DeserializeEvent::class => Events::DESERIALIZE,
PostDeserializeEvent::class => Events::POST_DESERIALIZE,
],
KernelEvents::VIEW => [
PreValidateEvent::class => Events::PRE_VALIDATE,
ValidateEvent::class => Events::VALIDATE,
PostValidateEvent::class => Events::POST_VALIDATE,

PreWriteEvent::class => Events::PRE_WRITE,
WriteEvent::class => Events::WRITE,
PostWriteEvent::class => Events::POST_WRITE,

PreSerializeEvent::class => Events::PRE_SERIALIZE,
SerializeEvent::class => Events::SERIALIZE,
PostSerializeEvent::class => Events::POST_SERIALIZE,

PreRespondEvent::class => Events::PRE_RESPOND,
RespondEvent::class => Events::RESPOND,
PostRespondEvent::class => Events::POST_RESPOND, // @todo kernel.response
],
KernelEvents::EXCEPTION => [
ValidationExceptionEvent::class => Events::VALIDATE_EXCEPTION,
],
];

private $dispatcher;

public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}

public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'dispatch',
KernelEvents::VIEW => 'dispatch',
];
}

public function dispatch(Event $event, string $eventName): void
{
$internalEventData = null;
$internalEventContext = [];

// case order is important because of Symfony events inheritance
switch (true) {
case $event instanceof GetResponseForControllerResultEvent:
$internalEventData = $event->getControllerResult();
$internalEventContext = ['request' => $event->getRequest()];
break;
case $event instanceof GetResponseForExceptionEvent:
$internalEventContext = ['request' => $event->getRequest(), 'exception' => $event->getException()];
break;
case $event instanceof GetResponseEvent:
$internalEventContext = ['request' => $event->getRequest()];
break;
}

foreach (self::INTERNAL_EVENTS_CONFIGURATION[$eventName] as $internalEventClass => $internalEventName) {
/** @var ApiPlatformEvent $internalEvent */
$internalEvent = new $internalEventClass($internalEventData, $internalEventContext);

$this->dispatcher->dispatch($internalEventName, $internalEvent);

$internalEventData = $internalEvent->getData();
// @todo set controller result?
if ($event instanceof GetResponseEvent && $response = $internalEvent->getContext()['response']) {
$event->setResponse($response);
}
}
}
}
21 changes: 14 additions & 7 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,21 @@
<service id="api_platform.path_segment_name_generator.underscore" class="ApiPlatform\Core\Operation\UnderscorePathSegmentNameGenerator" public="false" />
<service id="api_platform.path_segment_name_generator.dash" class="ApiPlatform\Core\Operation\DashPathSegmentNameGenerator" public="false" />

<!-- Event dispatcher -->

<service id="api_platform.dispatcher.internal_events" class="ApiPlatform\Core\Bridge\Symfony\Bundle\EventSubscriber\EventDispatcher">
<argument type="service" id="event_dispatcher" />

<tag name="kernel.event_subscriber" />
</service>

<!-- Event listeners -->

<!-- kernel.request priority must be < 8 to be executed after the Firewall -->
<service id="api_platform.listener.request.add_format" class="ApiPlatform\Core\EventListener\AddFormatListener">
<argument type="service" id="api_platform.negotiator" />
<argument type="service" id="api_platform.formats_provider" />

<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="7" />
<tag name="kernel.event_listener" event="api_platform.format_add" method="handleEvent" />
</service>

<service id="api_platform.listener.request.read" class="ApiPlatform\Core\EventListener\ReadListener">
Expand All @@ -155,35 +162,35 @@
<argument type="service" id="api_platform.serializer.context_builder" />
<argument type="service" id="api_platform.identifier.converter" />

<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="4" />
<tag name="kernel.event_listener" event="api_platform.read" method="handleEvent" />
</service>

<service id="api_platform.listener.view.write" class="ApiPlatform\Core\EventListener\WriteListener">
<argument type="service" id="api_platform.data_persister" />
<argument type="service" id="api_platform.iri_converter" on-invalid="null" />
<argument type="service" id="api_platform.metadata.resource.metadata_factory" on-invalid="null" />

<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="32" />
<tag name="kernel.event_listener" event="api_platform.write" method="handleEvent" />
</service>

<service id="api_platform.listener.request.deserialize" class="ApiPlatform\Core\EventListener\DeserializeListener">
<argument type="service" id="api_platform.serializer" />
<argument type="service" id="api_platform.serializer.context_builder" />
<argument type="service" id="api_platform.formats_provider" />

<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="2" />
<tag name="kernel.event_listener" event="api_platform.deserialize" method="handleEvent" />
</service>

<service id="api_platform.listener.view.serialize" class="ApiPlatform\Core\EventListener\SerializeListener">
<argument type="service" id="api_platform.serializer" />
<argument type="service" id="api_platform.serializer.context_builder" />

<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="16" />
<tag name="kernel.event_listener" event="api_platform.serialize" method="handleEvent" />
</service>

<service id="api_platform.listener.view.respond" class="ApiPlatform\Core\EventListener\RespondListener">
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="8" />
<tag name="kernel.event_listener" event="api_platform.respond" method="handleEvent" />
</service>

<service id="api_platform.listener.exception.validation" class="ApiPlatform\Core\Bridge\Symfony\Validator\EventListener\ValidationExceptionListener">
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Symfony/Bundle/Resources/config/fos_user.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<service id="api_platform.fos_user.event_listener" class="ApiPlatform\Core\Bridge\FosUser\EventListener">
<argument type="service" id="fos_user.user_manager" />

<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="24" />
<tag name="kernel.event_listener" event="api_platform.post_write" method="handleEvent" />
</service>
</services>

Expand Down
8 changes: 4 additions & 4 deletions src/Bridge/Symfony/Bundle/Resources/config/jsonapi.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,22 @@
<!-- Event listener -->

<service id="api_platform.jsonapi.listener.request.transform_pagination_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformPaginationParametersListener">
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
</service>

<service id="api_platform.jsonapi.listener.request.transform_sorting_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformSortingParametersListener">
<argument>%api_platform.collection.order_parameter_name%</argument>
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
</service>

<service id="api_platform.jsonapi.listener.request.transform_fieldsets_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformFieldsetsParametersListener">
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />

<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
</service>

<service id="api_platform.jsonapi.listener.request.transform_filtering_parameters" class="ApiPlatform\Core\JsonApi\EventListener\TransformFilteringParametersListener">
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="5" />
<tag name="kernel.event_listener" event="api_platform.pre_read" method="handleEvent" />
</service>
</services>

Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Symfony/Bundle/Resources/config/security.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<argument type="service" id="api_platform.security.resource_access_checker" />

<!-- This listener must be executed only when the current object is available -->
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="1" />
<tag name="kernel.event_listener" event="api_platform.post_deserialize" method="handleEvent" />
</service>

<service id="api_platform.security.expression_language_provider" class="ApiPlatform\Core\Security\Core\Authorization\ExpressionLanguageProvider" public="false">
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Symfony/Bundle/Resources/config/swagger-ui.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<services>

<service id="api_platform.swagger.listener.ui" class="ApiPlatform\Core\Bridge\Symfony\Bundle\EventListener\SwaggerUiListener">
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
<tag name="kernel.event_listener" event="api_platform.post_deserialize" method="handleEvent" />
</service>

<service id="api_platform.swagger.action.ui" class="ApiPlatform\Core\Bridge\Symfony\Bundle\Action\SwaggerUiAction" public="true">
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Symfony/Bundle/Resources/config/validator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
<argument type="service" id="api_platform.validator" />
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />

<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="64" />
<tag name="kernel.event_listener" event="api_platform.validate" method="handleEvent" />
</service>

<service id="api_platform.listener.view.validate_query_parameters" class="ApiPlatform\Core\Filter\QueryParameterValidateListener" public="false">
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<argument type="service" id="api_platform.filter_locator" />

<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="16" />
<tag name="kernel.event_listener" event="api_platform.query_parameter_validate" method="handleEvent" />
</service>
</services>

Expand Down
Loading

0 comments on commit 6c8b29f

Please sign in to comment.