Completed
Push — master ( 651d7b...ccffc6 )
by Dawid
02:51
created

DataCollector::onCheckResult()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 1
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiechu\SymfonyCommonsBundle\Service;
6
7
use Doctrine\Common\Annotations\Reader;
8
use Spiechu\SymfonyCommonsBundle\Annotation\Controller\ControllerAnnotationExtractorTrait;
9
use Spiechu\SymfonyCommonsBundle\Annotation\Controller\ResponseSchemaValidator;
10
use Spiechu\SymfonyCommonsBundle\Event\ApiVersion\ApiVersionSetEvent;
11
use Spiechu\SymfonyCommonsBundle\Event\ApiVersion\Events as ApiVersionEvents;
12
use Spiechu\SymfonyCommonsBundle\Event\ResponseSchemaCheck\CheckResult;
13
use Spiechu\SymfonyCommonsBundle\Event\ResponseSchemaCheck\Events as ResponseSchemaCheckEvents;
14
use Spiechu\SymfonyCommonsBundle\EventListener\RequestSchemaValidatorListener;
15
use Spiechu\SymfonyCommonsBundle\Service\SchemaValidator\ValidationViolation;
16
use Spiechu\SymfonyCommonsBundle\Twig\DataCollectorExtension;
17
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
21
use Symfony\Component\HttpKernel\DataCollector\DataCollector as BaseDataCollector;
22
use Symfony\Component\Routing\RouterInterface;
23
24
class DataCollector extends BaseDataCollector implements EventSubscriberInterface
25
{
26
    use ControllerAnnotationExtractorTrait;
27
28
    public const COLLECTOR_NAME = 'spiechu_symfony_commons.data_collector';
29
30
    /**
31
     * @var RouterInterface
32
     */
33
    protected $router;
34
35
    /**
36
     * @var Reader
37
     */
38
    protected $reader;
39
40
    /**
41
     * @var ControllerResolverInterface
42
     */
43
    protected $controllerResolver;
44
45
    /**
46
     * @var DataCollectorExtension
47
     */
48
    protected $dataCollectorExtension;
49
50
    /**
51
     * @param RouterInterface             $router
52
     * @param Reader                      $reader
53
     * @param ControllerResolverInterface $controllerResolver
54
     * @param DataCollectorExtension      $dataCollectorExtension
55
     */
56 12
    public function __construct(
57
        RouterInterface $router,
58
        Reader $reader,
59
        ControllerResolverInterface $controllerResolver,
60
        DataCollectorExtension $dataCollectorExtension
61
    ) {
62 12
        $this->router = $router;
63 12
        $this->reader = $reader;
64 12
        $this->controllerResolver = $controllerResolver;
65 12
        $this->dataCollectorExtension = $dataCollectorExtension;
66 12
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 12
    public function collect(Request $request, Response $response, \Exception $exception = null): void
72
    {
73 12
        $this->data['known_route_response_schemas'] = $request->attributes->has(RequestSchemaValidatorListener::ATTRIBUTE_RESPONSE_SCHEMAS)
74 2
            ? $request->attributes->get(RequestSchemaValidatorListener::ATTRIBUTE_RESPONSE_SCHEMAS)
75 10
            : null;
76
77 12
        $this->extractRoutesData();
78 12
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 12
    public function getName(): string
84
    {
85 12
        return static::COLLECTOR_NAME;
86
    }
87
88
    /**
89
     * Forward compatibility with Symfony 3.4.
90
     */
91
    public function reset(): void
92
    {
93
        $this->data = [];
94
    }
95
96
    /**
97
     * @return array
98
     */
99
    public function getGlobalResponseSchemas(): array
100
    {
101
        return $this->data['global_response_schemas'];
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 6
    public static function getSubscribedEvents(): array
108
    {
109
        return [
110 6
            ResponseSchemaCheckEvents::CHECK_RESULT => ['onCheckResult', 100],
111 6
            ApiVersionEvents::API_VERSION_SET => ['onApiVersionSet', 100],
112
        ];
113
    }
114
115
    /**
116
     * @param CheckResult $checkResult
117
     */
118 2
    public function onCheckResult(CheckResult $checkResult): void
119
    {
120 2
        $this->data['validation_result'] = $checkResult->getValidationResult();
121 2
    }
122
123
    /**
124
     * @param ApiVersionSetEvent $apiVersionSetEvent
125
     */
126 9
    public function onApiVersionSet(ApiVersionSetEvent $apiVersionSetEvent): void
127
    {
128 9
        $this->data['api_version_set'] = $apiVersionSetEvent->getApiVersion();
129 9
    }
130
131
    /**
132
     * @return array
133
     */
134
    public function getKnownRouteResponseSchemas(): array
135
    {
136
        return empty($this->data['known_route_response_schemas']) ? [] : $this->data['known_route_response_schemas'];
137
    }
138
139
    /**
140
     * @return int
141
     */
142
    public function getKnownRouteResponseSchemaNumber(): int
143
    {
144
        $counter = 0;
145
146
        foreach ($this->getKnownRouteResponseSchemas() as $format) {
147
            $counter += \count($format);
148
        }
149
150
        return $counter;
151
    }
152
153
    /**
154
     * @return int
155
     */
156
    public function getAllPotentialErrorsCount(): int
157
    {
158
        return \count($this->getValidationErrors()) + $this->getGlobalNonExistingSchemaFiles();
159
    }
160
161
    /**
162
     * @return bool
163
     */
164 2
    public function responseWasChecked(): bool
165
    {
166 2
        return array_key_exists('validation_result', $this->data);
167
    }
168
169
    /**
170
     * @return bool
171
     */
172 5
    public function apiVersionWasSet(): bool
173
    {
174 5
        return array_key_exists('api_version_set', $this->data);
175
    }
176
177
    /**
178
     * @return null|string
179
     */
180 5
    public function getApiVersion(): ?string
181
    {
182 5
        return $this->apiVersionWasSet() ? $this->data['api_version_set'] : null;
183
    }
184
185
    /**
186
     * @return ValidationViolation[]
187
     */
188
    public function getValidationErrors(): array
189
    {
190
        if (!$this->responseWasChecked()) {
191
            return [];
192
        }
193
194
        return $this->data['validation_result']->getViolations();
195
    }
196
197 12
    protected function extractRoutesData(): void
198
    {
199 12
        $this->data['global_response_schemas'] = [];
200 12
        $this->data['global_non_existing_schema_files'] = 0;
201
202 12
        foreach ($this->router->getRouteCollection() as $name => $route) {
203 12
            if (empty($controllerDefinition = $route->getDefault('_controller'))) {
204
                continue;
205
            }
206
207 12
            $methodAnnotation = $this->extractControllerResponseValidator($controllerDefinition);
208 12
            if (!$methodAnnotation instanceof ResponseSchemaValidator) {
209 12
                continue;
210
            }
211
212 11
            $annotationSchemas = $methodAnnotation->getSchemas();
213
214 11
            $this->data['global_response_schemas'][] = [
215 11
                'path' => $route->getPath(),
216 11
                'name' => $name,
217 11
                'controller' => $controllerDefinition,
218 11
                'response_schemas' => $annotationSchemas,
219
            ];
220
221 11
            foreach ($annotationSchemas as $schemas) {
222 11
                foreach ($schemas as $schema) {
223 11
                    if (!$this->dataCollectorExtension->schemaFileExists($schema)) {
224 11
                        ++$this->data['global_non_existing_schema_files'];
225
                    }
226
                }
227
            }
228
        }
229 12
    }
230
231
    /**
232
     * @param string $controllerDefinition
233
     *
234
     * @throws \Exception
235
     *
236
     * @return null|ResponseSchemaValidator
237
     */
238 12
    protected function extractControllerResponseValidator(string $controllerDefinition): ?ResponseSchemaValidator
239
    {
240 12
        $resolvedController = $this->controllerResolver->getController(new Request(
241 12
            [],
242 12
            [],
243
            [
244 12
                '_controller' => $controllerDefinition,
245
            ]
246
        ));
247
248 12
        if (!\is_callable($resolvedController)) {
249
            return null;
250
        }
251
252 12
        return $this->getMethodAnnotationFromController($resolvedController, ResponseSchemaValidator::class);
253
    }
254
255
    /**
256
     * @return int
257
     */
258
    protected function getGlobalNonExistingSchemaFiles(): int
259
    {
260
        return empty($this->data['global_non_existing_schema_files']) ? 0 : \count($this->data['global_non_existing_schema_files']);
261
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266 12
    protected function getAnnotationReader(): Reader
267
    {
268 12
        return $this->reader;
269
    }
270
}
271