Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.87% covered (success)
97.87%
46 / 47
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
AddEntityCacheHeadersEventSubscriber
97.87% covered (success)
97.87%
46 / 47
66.67% covered (warning)
66.67%
2 / 3
15
0.00% covered (danger)
0.00%
0 / 1
 getSubscribedEvents
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 onApieResourceEvent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 onApieResponseCreated
97.30% covered (success)
97.30%
36 / 37
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2namespace Apie\RestApi\EventListeners;
3
4use Apie\Common\Actions\RemoveObjectAction;
5use Apie\Common\Events\ApieResourceCreated;
6use Apie\Common\Events\ApieResourceMethodCalled;
7use Apie\Common\Events\ApieResourceModified;
8use Apie\Common\Events\ApieResourceRead;
9use Apie\Common\Events\ApieResponseCreated;
10use Apie\Core\BoundedContext\BoundedContextId;
11use Apie\Core\ContextConstants;
12use Apie\Core\Datalayers\ApieDatalayer;
13use Apie\Core\Datalayers\DataLayerMetadata;
14use Apie\Core\Entities\EntityInterface;
15use Apie\Core\IdentifierUtils;
16use Symfony\Component\EventDispatcher\EventSubscriberInterface;
17
18class AddEntityCacheHeadersEventSubscriber implements EventSubscriberInterface
19{
20    private ?EntityInterface $resource = null;
21
22    public static function getSubscribedEvents(): array
23    {
24        return [
25            ApieResourceCreated::class => 'onApieResourceEvent',
26            ApieResourceRead::class => 'onApieResourceEvent',
27            ApieResourceModified::class => 'onApieResourceEvent',
28            ApieResourceMethodCalled::class => 'onApieResourceEvent',
29            ApieResponseCreated::class      => 'onApieResponseCreated',
30        ];
31    }
32
33    public function onApieResourceEvent(
34        ApieResourceCreated|ApieResourceRead|ApieResourceModified|ApieResourceMethodCalled $event
35    ): void {
36        if (!$event->context->getContext(ContextConstants::REST_API, false)) {
37            return;
38        }
39        $this->resource = $event->resource;
40    }
41
42    public function onApieResponseCreated(ApieResponseCreated $event): void
43    {
44        $context = $event->context;
45        $action = $context->getContext(ContextConstants::APIE_ACTION, false);
46        $dataLayer = $context->getContext(ApieDatalayer::class, false);
47        if (
48            $action === RemoveObjectAction::class
49            || !$dataLayer instanceof ApieDatalayer
50            || !$context->getContext(ContextConstants::REST_API, false)
51            || $event->response->getStatusCode() >= 300
52            || $event->response->getStatusCode() === 204
53        ) {
54            $this->resource = null;
55            return;
56        }
57        if ($event->response->hasHeader('ETag')) {
58            $event->response = $event->response->withHeader('Cache-Control', 'no-cache');
59            $this->resource = null;
60            return;
61        }
62
63        $resource = $context->getContext(ContextConstants::RESOURCE, false);
64        if ($resource instanceof EntityInterface) {
65            $identifier = $resource->getId();
66        } elseif ($this->resource) {
67            $identifier = $this->resource->getId();
68        } else {
69            $resourceId = $context->getContext(ContextConstants::RESOURCE_ID, false);
70            if (!is_string($resourceId)) {
71                $this->resource = null;
72                return;
73            }
74            $identifier = IdentifierUtils::idStringToIdentifier($resourceId, $context, false);
75            if ($identifier === null) {
76                $this->resource = null;
77                return;
78            }
79        }
80        $this->resource = null;
81
82        $boundedContextId = new BoundedContextId(
83            $context->getContext(ContextConstants::BOUNDED_CONTEXT_ID, false) ?? 'default'
84        );
85        $metadata = $context->getContext(DataLayerMetadata::class, false);
86        if (!$metadata instanceof DataLayerMetadata) {
87            $metadata = $dataLayer->getMetadata($identifier, $boundedContextId);
88        }
89        $event->response = $event->response
90            ->withHeader('ETag', '"' . $metadata->etag . '"')
91            ->withHeader('Cache-Control', 'no-cache');
92    }
93}