Skip to content

Commit

Permalink
Merge 12ee47d into 22ce639
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaumedelre committed Aug 6, 2020
2 parents 22ce639 + 12ee47d commit 6f5cb86
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 3 deletions.
10 changes: 10 additions & 0 deletions src/Annotation/ApiResource.php
Expand Up @@ -27,6 +27,7 @@
* @Attribute("accessControlMessage", type="string"),
* @Attribute("attributes", type="array"),
* @Attribute("cacheHeaders", type="array"),
* @Attribute("cacheInvalidation", type="bool"),
* @Attribute("collectionOperations", type="array"),
* @Attribute("denormalizationContext", type="array"),
* @Attribute("deprecationReason", type="string"),
Expand Down Expand Up @@ -87,6 +88,7 @@ final class ApiResource
'securityPostDenormalize',
'securityPostDenormalizeMessage',
'cacheHeaders',
'cacheInvalidation',
'collectionOperations',
'denormalizationContext',
'deprecationReason',
Expand Down Expand Up @@ -175,6 +177,14 @@ final class ApiResource
*/
private $cacheHeaders;

/**
* @see https://api-platform.com/docs/core/performance/#enabling-disabling-http-cache-invalidation
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
*
* @var bool
*/
private $cacheInvalidation;

/**
* @see https://api-platform.com/docs/core/serialization/#using-serialization-groups
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
Expand Down
Expand Up @@ -10,7 +10,7 @@

<service id="api_platform.http_cache.listener.response.add_tags" class="ApiPlatform\Core\HttpCache\EventListener\AddTagsListener">
<argument type="service" id="api_platform.iri_converter" />

<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" priority="-2" />
</service>
</services>
Expand Down
12 changes: 11 additions & 1 deletion src/HttpCache/EventListener/AddTagsListener.php
Expand Up @@ -14,6 +14,8 @@
namespace ApiPlatform\Core\HttpCache\EventListener;

use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ToggleableOperationAttributeTrait;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

Expand All @@ -30,11 +32,17 @@
*/
final class AddTagsListener
{
use ToggleableOperationAttributeTrait;

public const OPERATION_ATTRIBUTE_KEY = 'cache_invalidation';

private $iriConverter;
private $resourceMetadataFactory;

public function __construct(IriConverterInterface $iriConverter)
public function __construct(IriConverterInterface $iriConverter, ResourceMetadataFactoryInterface $resourceMetadataFactory = null)
{
$this->iriConverter = $iriConverter;
$this->resourceMetadataFactory = $resourceMetadataFactory;
}

/**
Expand All @@ -49,6 +57,8 @@ public function onKernelResponse(ResponseEvent $event): void
!$request->isMethodCacheable()
|| !$response->isCacheable()
|| (!$attributes = RequestAttributesExtractor::extractAttributes($request))
|| !$attributes['cache_invalidation']
|| $this->isOperationAttributeDisabled($attributes, self::OPERATION_ATTRIBUTE_KEY)
) {
return;
}
Expand Down
1 change: 1 addition & 0 deletions src/Util/AttributesExtractor.php
Expand Up @@ -61,6 +61,7 @@ public static function extractAttributes(array $attributes): array
'receive' => (bool) ($attributes['_api_receive'] ?? true),
'respond' => (bool) ($attributes['_api_respond'] ?? true),
'persist' => (bool) ($attributes['_api_persist'] ?? true),
'cache_invalidation' => (bool) ($attributes['_api_cache_invalidation'] ?? true),
];

return $result;
Expand Down
Expand Up @@ -142,6 +142,7 @@ public function testWithResource()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
], $dataCollector->getRequestAttributes());
$this->assertSame(['foo', 'bar'], $dataCollector->getAcceptableContentTypes());
$this->assertSame(DummyEntity::class, $dataCollector->getResourceClass());
Expand Down
70 changes: 69 additions & 1 deletion tests/HttpCache/EventListener/AddTagsListenerTest.php
Expand Up @@ -14,11 +14,20 @@
namespace ApiPlatform\Core\Tests\HttpCache\EventListener;

use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
use ApiPlatform\Core\EventListener\ReadListener;
use ApiPlatform\Core\HttpCache\EventListener\AddTagsListener;
use ApiPlatform\Core\Identifier\IdentifierConverterInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

/**
Expand Down Expand Up @@ -105,11 +114,70 @@ public function testDoNotSetHeaderWhenEmptyTagList()
$this->assertFalse($response->headers->has('Cache-Tags'));
}

public function testDoNotAddTagsWhenCacheInvalidationFlagIsFalse()
{
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);

$request = new Request([], [], ['_resources' => ['/foo', '/bar'], '_api_resource_class' => Dummy::class, '_api_item_operation_name' => 'get', '_api_cache_invalidation' => false]);

$response = new Response();
$response->setPublic();
$response->setEtag('foo');

$event = $this->prophesize(ResponseEvent::class);
$event->getRequest()->willReturn($request)->shouldBeCalled();
$event->getResponse()->willReturn($response)->shouldBeCalled();

$listener = new AddTagsListener($iriConverterProphecy->reveal());
$listener->onKernelResponse($event->reveal());

$this->assertFalse($response->headers->has('Cache-Tags'));
}

public function testDoNotAddTagsWhenDisabledInOperationAttribute()
{
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);

$resourceMetadata = new ResourceMetadata('Dummy', null, null, [
'get' => [
'cache_invalidation' => false,
],
]);

$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn($resourceMetadata);

$request = new Request([], [], ['_resources' => ['/foo', '/bar'], '_api_resource_class' => Dummy::class, '_api_item_operation_name' => 'get']);

$response = new Response();
$response->setPublic();
$response->setEtag('foo');

$event = $this->prophesize(ResponseEvent::class);
$event->getRequest()->willReturn($request)->shouldBeCalled();
$event->getResponse()->willReturn($response)->shouldBeCalled();

$listener = new AddTagsListener($iriConverterProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal());
$listener->onKernelResponse($event->reveal());

$this->assertFalse($response->headers->has('Cache-Tags'));
}

public function testAddTags()
{
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);

$resourceMetadata = new ResourceMetadata('Dummy', null, null, [
'get' => [
'cache_invalidation' => true,
],
]);

$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn($resourceMetadata);

$request = new Request([], [], ['_resources' => ['/foo', '/bar'], '_api_resource_class' => Dummy::class, '_api_item_operation_name' => 'get']);
$request->setMethod('GET');

$response = new Response();
$response->setPublic();
Expand All @@ -119,7 +187,7 @@ public function testAddTags()
$event->getRequest()->willReturn($request)->shouldBeCalled();
$event->getResponse()->willReturn($response)->shouldBeCalled();

$listener = new AddTagsListener($iriConverterProphecy->reveal());
$listener = new AddTagsListener($iriConverterProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal());
$listener->onKernelResponse($event->reveal());

$this->assertSame('/foo,/bar', $response->headers->get('Cache-Tags'));
Expand Down
56 changes: 56 additions & 0 deletions tests/Util/RequestAttributesExtractorTest.php
Expand Up @@ -33,6 +33,7 @@ public function testExtractCollectionAttributes()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -49,6 +50,7 @@ public function testExtractItemAttributes()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -65,6 +67,7 @@ public function testExtractReceive()
'receive' => false,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -78,6 +81,7 @@ public function testExtractReceive()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -91,6 +95,7 @@ public function testExtractReceive()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -107,6 +112,7 @@ public function testExtractRespond()
'receive' => true,
'respond' => false,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -120,6 +126,7 @@ public function testExtractRespond()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -133,6 +140,7 @@ public function testExtractRespond()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -149,6 +157,7 @@ public function testExtractPersist()
'receive' => true,
'respond' => true,
'persist' => false,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -162,6 +171,7 @@ public function testExtractPersist()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand All @@ -175,6 +185,52 @@ public function testExtractPersist()
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
}

public function testExtractAddTags()
{
$request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_item_operation_name' => 'get', '_api_cache_invalidation' => '0']);

$this->assertEquals(
[
'resource_class' => 'Foo',
'item_operation_name' => 'get',
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => false,
],
RequestAttributesExtractor::extractAttributes($request)
);

$request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_item_operation_name' => 'get', '_api_cache_invalidation' => '1']);

$this->assertEquals(
[
'resource_class' => 'Foo',
'item_operation_name' => 'get',
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);

$request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_item_operation_name' => 'get']);

$this->assertEquals(
[
'resource_class' => 'Foo',
'item_operation_name' => 'get',
'receive' => true,
'respond' => true,
'persist' => true,
'cache_invalidation' => true,
],
RequestAttributesExtractor::extractAttributes($request)
);
Expand Down

0 comments on commit 6f5cb86

Please sign in to comment.