Completed
Branch v4 (4e54dd)
by Pieter
03:26
created

PaginationPlugin::onPostCreateResponse()   B

Complexity

Conditions 7
Paths 14

Size

Total Lines 27
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 7
eloc 20
nc 14
nop 1
dl 0
loc 27
rs 8.6666
c 1
b 0
f 1
1
<?php
2
3
namespace W2w\Lib\Apie\Plugins\Pagination;
4
5
use erasys\OpenApi\Spec\v3\Document;
6
use erasys\OpenApi\Spec\v3\Header;
7
use erasys\OpenApi\Spec\v3\Operation;
8
use erasys\OpenApi\Spec\v3\PathItem;
9
use erasys\OpenApi\Spec\v3\Reference;
10
use Pagerfanta\Adapter\AdapterInterface;
11
use Pagerfanta\Adapter\ArrayAdapter;
12
use Pagerfanta\Pagerfanta;
13
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterHelper;
14
use W2w\Lib\Apie\Events\DecodeEvent;
15
use W2w\Lib\Apie\Events\DeleteResourceEvent;
16
use W2w\Lib\Apie\Events\ModifySingleResourceEvent;
17
use W2w\Lib\Apie\Events\NormalizeEvent;
18
use W2w\Lib\Apie\Events\ResponseAllEvent;
19
use W2w\Lib\Apie\Events\ResponseEvent;
20
use W2w\Lib\Apie\Events\RetrievePaginatedResourcesEvent;
21
use W2w\Lib\Apie\Events\RetrieveSingleResourceEvent;
22
use W2w\Lib\Apie\Events\StoreExistingResourceEvent;
23
use W2w\Lib\Apie\Events\StoreNewResourceEvent;
24
use W2w\Lib\Apie\PluginInterfaces\ApieAwareInterface;
25
use W2w\Lib\Apie\PluginInterfaces\ApieAwareTrait;
26
use W2w\Lib\Apie\PluginInterfaces\NormalizerProviderInterface;
27
use W2w\Lib\Apie\PluginInterfaces\OpenApiEventProviderInterface;
28
use W2w\Lib\Apie\PluginInterfaces\ResourceLifeCycleInterface;
29
use W2w\Lib\Apie\Plugins\Pagination\Normalizers\PaginatorNormalizer;
30
31
class PaginationPlugin implements ResourceLifeCycleInterface, NormalizerProviderInterface, OpenApiEventProviderInterface, ApieAwareInterface
32
{
33
    use ApieAwareTrait;
34
35
    const PREV_HEADER = 'x-pagination-previous';
36
37
    const NEXT_HEADER = 'x-pagination-next';
38
39
    const FIRST_HEADER = 'x-pagination-first';
40
41
    const LAST_HEADER = 'x-pagination-last';
42
43
    const COUNT_HEADER = 'x-pagination-count';
44
45
    public function getNormalizers(): array
46
    {
47
        return [new PaginatorNormalizer()];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array(new W2w\Lib...\PaginatorNormalizer()) returns the type array<integer,W2w\Lib\Ap...rs\PaginatorNormalizer> which is incompatible with the return type mandated by W2w\Lib\Apie\PluginInter...rface::getNormalizers() of Symfony\Component\Serial...DenormalizerInterface[].

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
48
    }
49
50
    public function onOpenApiDocGenerated(Document $document): Document
51
    {
52
        /** @var PathItem[] $paths */
53
        $paths = $document->paths ?? [];
54
        foreach ($paths as $url => $path) {
55
            if (strpos($url, '{id}', 0) === false && $path->get) {
56
                $this->patch($path->get);
57
            }
58
        }
59
        return $document;
60
    }
61
62
    private function patch(Operation $operation): Operation
63
    {
64
        foreach ($operation->responses as &$response) {
65
            if ($response instanceof Reference) {
66
                continue;
67
            }
68
            $response->headers[self::PREV_HEADER] = new Header('Pagination previous page url', []);
69
            $response->headers[self::NEXT_HEADER] = new Header('Pagination next page url', []);
70
            $response->headers[self::FIRST_HEADER] = new Header('Pagination first page url', []);
71
            $response->headers[self::LAST_HEADER] = new Header('Pagination last page url', []);
72
        }
73
        return $operation;
74
    }
75
76
    public function onPreDeleteResource(DeleteResourceEvent $event)
77
    {
78
    }
79
80
    public function onPostDeleteResource(DeleteResourceEvent $event)
81
    {
82
    }
83
84
    public function onPreRetrieveResource(RetrieveSingleResourceEvent $event)
85
    {
86
    }
87
88
    public function onPostRetrieveResource(RetrieveSingleResourceEvent $event)
89
    {
90
    }
91
92
    public function onPreRetrieveAllResources(RetrievePaginatedResourcesEvent $event)
93
    {
94
    }
95
96
    public function onPostRetrieveAllResources(RetrievePaginatedResourcesEvent $event)
97
    {
98
    }
99
100
    public function onPrePersistExistingResource(StoreExistingResourceEvent $event)
101
    {
102
    }
103
104
    public function onPostPersistExistingResource(StoreExistingResourceEvent $event)
105
    {
106
    }
107
108
    public function onPreDecodeRequestBody(DecodeEvent $event)
109
    {
110
    }
111
112
    public function onPostDecodeRequestBody(DecodeEvent $event)
113
    {
114
    }
115
116
    public function onPreModifyResource(ModifySingleResourceEvent $event)
117
    {
118
    }
119
120
    public function onPostModifyResource(ModifySingleResourceEvent $event)
121
    {
122
    }
123
124
    public function onPreCreateResource(StoreNewResourceEvent $event)
125
    {
126
    }
127
128
    public function onPostCreateResource(StoreNewResourceEvent $event)
129
    {
130
    }
131
132
    public function onPrePersistNewResource(StoreExistingResourceEvent $event)
133
    {
134
    }
135
136
    public function onPostPersistNewResource(StoreExistingResourceEvent $event)
137
    {
138
    }
139
140
    public function onPreCreateResponse(ResponseEvent $event)
141
    {
142
    }
143
144
    public function onPostCreateResponse(ResponseEvent $event)
145
    {
146
        if (!($event instanceof ResponseAllEvent)) {
147
            return;
148
        }
149
        $resource = $event->getResource();
150
        if (!($resource instanceof Pagerfanta)) {
151
            if (is_array($resource)) {
152
                $resource = new Pagerfanta(new ArrayAdapter($resource));
153
            } else if (is_iterable($resource)) {
154
                $resource = new Pagerfanta(new ArrayAdapter(iterator_to_array($resource)));
155
            } else {
156
                return;
157
            }
158
            $event->getSearchFilterRequest()->updatePaginator($resource);
159
        }
160
        $response = $event->getResponse()
161
            ->withHeader(self::FIRST_HEADER, $this->generateUrl($event, 0))
162
            ->withHeader(self::LAST_HEADER, $this->generateUrl($event, $resource->getNbPages() - 1))
163
            ->withHeader(self::COUNT_HEADER, $this->generateUrl($event, $resource->getNbPages()));
164
        if ($resource->hasPreviousPage()) {
165
            $response = $response->withHeader(self::PREV_HEADER, $this->generateUrl($event, $resource->getPreviousPage()));
166
        }
167
        if ($resource->hasNextPage()) {
168
            $response = $response->withHeader(self::NEXT_HEADER, $this->generateUrl($event, $resource->getNextPage()));
169
        }
170
        $event->setResponse($response);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type null; however, parameter $response of W2w\Lib\Apie\Events\ResponseEvent::setResponse() does only seem to accept Psr\Http\Message\ResponseInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
        $event->setResponse(/** @scrutinizer ignore-type */ $response);
Loading history...
171
    }
172
173
    private function generateUrl(ResponseAllEvent  $event, int $page)
174
    {
175
        $baseUrl = $this->getApie()->getOverviewUrlForResourceClass($event->getResourceClass(), $event->getSearchFilterRequest());
176
        return $baseUrl . '?' . http_build_query(['page' => $page, 'limit' => $event->getSearchFilterRequest()->getNumberOfItems()]);
177
    }
178
179
    public function onPreCreateNormalizedData(NormalizeEvent $event)
180
    {
181
    }
182
183
    public function onPostCreateNormalizedData(NormalizeEvent $event)
184
    {
185
    }
186
}
187