From 4e572a86d20927d92af5e9a46a03943b145b9b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81=20R?= Date: Mon, 27 Nov 2017 08:45:01 +0100 Subject: [PATCH] EZP-28183: Fix REST http cache on move operations (and more) With this bundle, REST HttpCache is not cleared when moving content, editing content types, (...) And given the time it will take to fix REST server to return data that can be used for ResponseTaggers and how this also affects custum responses given, as we now only purge by specific tags. Adding this is thus safests. This somewhat brings back what @Plopix suggested in #21, essentially making sure we add all tags also to X-Location responses by loading location, but to be on the safe side it does it using sudo() and catching NotFound. Opted not to reuse ReponseTaggers here as they don't fully fit, and as we want to rewrite this header independently of if view cache is enabled or not. --- .../XLocationIdResponseSubscriberSpec.php | 66 ++++++++++++++++--- .../XLocationIdResponseSubscriber.php | 38 +++++++++-- src/Resources/config/view_cache.yml | 2 +- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php b/spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php index e53fb405..4a909bcc 100644 --- a/spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php +++ b/spec/EventSubscriber/XLocationIdResponseSubscriberSpec.php @@ -5,8 +5,12 @@ */ namespace spec\EzSystems\PlatformHttpCacheBundle\EventSubscriber; +use eZ\Publish\API\Repository\Values\Content\ContentInfo; +use eZ\Publish\Core\Base\Exceptions\NotFoundException; +use eZ\Publish\Core\Repository\Repository; +use eZ\Publish\Core\Repository\Values\Content\Location; use PhpSpec\ObjectBehavior; -use Prophecy\Argument; +use Prophecy\Argument\Token\AnyValueToken; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; @@ -18,12 +22,13 @@ public function let( FilterResponseEvent $event, Response $response, TagHandlerInterface $tagHandler, + Repository $repository, ResponseHeaderBag $responseHeaders ) { $response->headers = $responseHeaders; $event->getResponse()->willReturn($response); - $this->beConstructedWith($tagHandler); + $this->beConstructedWith($tagHandler, $repository); } public function it_does_not_rewrite_header_if_there_is_none( @@ -36,31 +41,72 @@ public function it_does_not_rewrite_header_if_there_is_none( $this->rewriteCacheHeader($event); } - public function it_rewrite_header_if_there( + public function it_rewrite_header_with_location_info( FilterResponseEvent $event, + Response $response, + TagHandlerInterface $tagHandler, + Repository $repository, ResponseHeaderBag $responseHeaders ) { $responseHeaders->has('X-Location-Id')->willReturn(true); $responseHeaders->get('X-Location-Id')->willReturn('123'); - $responseHeaders->has('Surrogate-Key')->willReturn(false); - $responseHeaders->set('Surrogate-Key', ['location-123'])->willReturn(null); - $responseHeaders->remove('X-Location-Id')->willReturn(null); + $repository->sudo(new AnyValueToken())->willReturn( + new Location([ + 'id' => 123, + 'parentLocationId' => 2, + 'pathString' => '/1/2/123/', + 'contentInfo' => new ContentInfo(['id' => 101, 'contentTypeId' => 3, 'mainLocationId' => 120]) + ]) + ); + + $tagHandler->addTagHeaders($response, [ + 'location-123', + 'parent-2', + 'path-1', + 'path-2', + 'path-123', + 'content-101', + 'content-type-3', + 'location-120', + ])->shouldBecalled(); + $responseHeaders->remove('X-Location-Id')->shouldBecalled(); + + $this->rewriteCacheHeader($event); + } + + public function it_rewrite_header_on_not_found_location( + FilterResponseEvent $event, + Response $response, + TagHandlerInterface $tagHandler, + Repository $repository, + ResponseHeaderBag $responseHeaders + ) { + $responseHeaders->has('X-Location-Id')->willReturn(true); + $responseHeaders->get('X-Location-Id')->willReturn('123'); + + $repository->sudo(new AnyValueToken())->willThrow(new NotFoundException('id', 123)); + + $tagHandler->addTagHeaders($response, ['location-123', 'path-123'])->shouldBecalled(); + $responseHeaders->remove('X-Location-Id')->shouldBecalled(); $this->rewriteCacheHeader($event); } public function it_rewrite_header_also_in_unofficial_plural_form_and_merges_exisitng_value( FilterResponseEvent $event, + Response $response, + TagHandlerInterface $tagHandler, + Repository $repository, ResponseHeaderBag $responseHeaders ) { $responseHeaders->has('X-Location-Id')->willReturn(true); $responseHeaders->get('X-Location-Id')->willReturn('123,34'); - $responseHeaders->has('Surrogate-Key')->willReturn(true); - $responseHeaders->get('Surrogate-Key', null, false)->willReturn(['content-44']); - $responseHeaders->set('Surrogate-Key', ['content-44', 'location-123', 'location-34'])->willReturn(null); - $responseHeaders->remove('X-Location-Id')->willReturn(null); + $repository->sudo(new AnyValueToken())->willThrow(new NotFoundException('id', 123)); + + $tagHandler->addTagHeaders($response, ['location-123', 'path-123', 'location-34', 'path-34'])->shouldBecalled(); + $responseHeaders->remove('X-Location-Id')->shouldBecalled(); $this->rewriteCacheHeader($event); } diff --git a/src/EventSubscriber/XLocationIdResponseSubscriber.php b/src/EventSubscriber/XLocationIdResponseSubscriber.php index 3df256e0..9f6f142c 100644 --- a/src/EventSubscriber/XLocationIdResponseSubscriber.php +++ b/src/EventSubscriber/XLocationIdResponseSubscriber.php @@ -5,6 +5,8 @@ */ namespace EzSystems\PlatformHttpCacheBundle\EventSubscriber; +use eZ\Publish\API\Repository\Exceptions\NotFoundException; +use eZ\Publish\API\Repository\Repository; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -22,14 +24,16 @@ class XLocationIdResponseSubscriber implements EventSubscriberInterface { const LOCATION_ID_HEADER = 'X-Location-Id'; - /** - * @var EzSystems\PlatformHttpCacheBundle\Handler\TagHandlerInterface - */ + /** @var \EzSystems\PlatformHttpCacheBundle\Handler\TagHandlerInterface */ private $tagHandler; - public function __construct(TagHandlerInterface $tagHandler) + /** @var \eZ\Publish\API\Repository\Repository */ + private $repository; + + public function __construct(TagHandlerInterface $tagHandler, Repository $repository) { $this->tagHandler = $tagHandler; + $this->repository = $repository; } public static function getSubscribedEvents() @@ -53,8 +57,30 @@ public function rewriteCacheHeader(FilterResponseEvent $event) $tags = []; foreach (explode(',', $response->headers->get(static::LOCATION_ID_HEADER)) as $id) { $id = trim($id); - $tags[] = "location-$id"; - $tags[] = "path-$id"; + try { + /** @var $location \eZ\Publish\API\Repository\Values\Content\Location */ + $location = $this->repository->sudo(function (Repository $repository) use ($id) { + return $repository->getLocationService()->loadLocation($id); + }); + + $tags[] = 'location-' . $location->id; + $tags[] = 'parent-' . $location->parentLocationId; + + foreach ($location->path as $pathItem) { + $tags[] = 'path-' . $pathItem; + } + + $contentInfo = $location->getContentInfo(); + $tags[] = 'content-' . $contentInfo->id; + $tags[] = 'content-type-' . $contentInfo->contentTypeId; + + if ($contentInfo->mainLocationId !== $location->id) { + $tags[] = 'location-' . $contentInfo->mainLocationId; + } + } catch (NotFoundException $e) { + $tags[] = "location-$id"; + $tags[] = "path-$id"; + } } $this->tagHandler->addTagHeaders($response, array_unique($tags)); diff --git a/src/Resources/config/view_cache.yml b/src/Resources/config/view_cache.yml index ee392b30..275d20a1 100644 --- a/src/Resources/config/view_cache.yml +++ b/src/Resources/config/view_cache.yml @@ -9,7 +9,7 @@ services: ezplatform.x_location_id.response_subscriber: class: EzSystems\PlatformHttpCacheBundle\EventSubscriber\XLocationIdResponseSubscriber - arguments: ['@ezplatform.http_cache.tag_handler'] + arguments: ['@ezplatform.http_cache.tag_handler', '@ezpublish.api.repository'] tags: - { name: kernel.event_subscriber }