GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( b5d278...796de0 )
by Sergey
17s queued 11s
created

JsonApiService   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 338
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 19

Test Coverage

Coverage 89.17%

Importance

Changes 0
Metric Value
wmc 39
lcom 2
cbo 19
dl 0
loc 338
ccs 107
cts 120
cp 0.8917
rs 9.28
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A getFactory() 0 4 1
A parseRequest() 0 25 3
B validateRequest() 0 31 7
A getResponseFactory() 0 4 1
A getRequestEnvironment() 0 16 3
A initializeEnvironment() 0 11 1
A createMatcher() 0 15 5
A parseMediaTypeString() 0 9 2
A parseRequestHeaders() 0 8 1
A createPsr7Request() 0 19 2
A parseQuery() 0 15 3
A parseBody() 0 13 3
A validateData() 0 4 2
A registerDecoders() 0 9 2
A registerEncoders() 0 9 2
1
<?php
2
/*
3
 * This file is part of the reva2/jsonapi.
4
 *
5
 * (c) Sergey Revenko <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Reva2\JsonApi\Services;
12
13
use Neomerx\JsonApi\Contracts\Codec\CodecMatcherInterface;
14
use Neomerx\JsonApi\Contracts\Schema\ContainerInterface;
15
use Neomerx\JsonApi\Document\Error;
16
use Neomerx\JsonApi\Exceptions\JsonApiException;
17
use Reva2\JsonApi\Contracts\Decoders\DataParserInterface;
18
use Reva2\JsonApi\Contracts\Decoders\DecoderInterface;
19
use Reva2\JsonApi\Contracts\Factories\FactoryInterface;
20
use Reva2\JsonApi\Contracts\Http\Query\QueryParametersParserInterface;
21
use Reva2\JsonApi\Contracts\Http\RequestInterface;
22
use Reva2\JsonApi\Contracts\Services\EnvironmentInterface;
23
use Reva2\JsonApi\Contracts\Services\JsonApiRegistryInterface;
24
use Reva2\JsonApi\Contracts\Services\JsonApiServiceInterface;
25
use Reva2\JsonApi\Contracts\Services\ValidationServiceInterface;
26
use Reva2\JsonApi\Http\ResponseFactory;
27
use Symfony\Component\HttpFoundation\Request;
28
use Neomerx\JsonApi\Http\Request as Psr7Request;
29
30
/**
31
 * Service for JSON API requests processing
32
 *
33
 * @package Reva2\JsonApi\Services
34
 * @author Sergey Revenko <[email protected]>
35
 */
36
class JsonApiService implements JsonApiServiceInterface
37
{
38
    /**
39
     * @var FactoryInterface
40
     */
41
    protected $factory;
42
43
    /**
44
     * @var JsonApiRegistryInterface
45
     */
46
    protected $registry;
47
48
    /**
49
     * @var ContainerInterface
50
     */
51
    protected $schemas;
52
53
    /**
54
     * @var ValidationServiceInterface
55
     */
56
    protected $validator;
57
58
    /**
59
     * @var DataParserInterface
60
     */
61
    protected $parser;
62
63
    /**
64
     * Constructor
65
     *
66
     * @param FactoryInterface $factory
67
     * @param JsonApiRegistryInterface $registry
68
     * @param ContainerInterface $schemas
69
     * @param DataParserInterface $parser
70
     * @param ValidationServiceInterface $validator
71
     */
72 6
    public function __construct(
73
        FactoryInterface $factory,
74
        JsonApiRegistryInterface $registry,
75
        ContainerInterface $schemas,
76
        DataParserInterface $parser,
77
        ValidationServiceInterface $validator
78
    ) {
79 6
        $this->factory = $factory;
80 6
        $this->registry = $registry;
81 6
        $this->parser = $parser;
82 6
        $this->schemas = $schemas;
83 6
        $this->validator = $validator;
84 6
    }
85
86
    /**
87
     * @inheritdoc
88
     */
89
    public function getFactory()
90
    {
91
        return $this->factory;
92
    }
93
94
    /**
95
     * @inheritdoc
96
     */
97 6
    public function parseRequest(Request $request, EnvironmentInterface $environment = null)
98
    {
99 6
        if (null === $environment) {
100 6
            $environment = $this->getRequestEnvironment($request);
101
        }
102
103 4
        $this->initializeEnvironment($environment, $request);
104
105 3
        $prevGroups = $this->parser->getSerializationGroups();
106
107 3
        $this->parser->setSerializationGroups($environment->getSerializationGroups());
108
109 3
        $apiRequest = $this->factory->createRequest($environment);
110
        $apiRequest
111 3
            ->setQuery($this->parseQuery($request, $environment))
112 3
            ->setBody($this->parseBody($request, $environment));
113
114 3
        $this->parser->setSerializationGroups($prevGroups);
115
116 3
        if (null !== $environment->getValidationGroups()) {
117 2
            $this->validateRequest($apiRequest);
118
        }
119
120 3
        return $apiRequest;
121
    }
122
123
    /**
124
     * @inheritdoc
125
     */
126 2
    public function validateRequest(RequestInterface $request)
127
    {
128 2
        $validationGroups = $request->getEnvironment()->getValidationGroups();
129 2
        if (is_bool($validationGroups)) {
130
            if (false === $validationGroups) {
131
                return;
132
            } else {
133
                $validationGroups = null;
134
            }
135
        }
136
137 2
        $errors = $this->validateData($request->getQuery(), $validationGroups);
138 2
        $errors = array_merge($errors, $this->validateData($request->getBody(), $validationGroups));
139
140 2
        if (0 === count($errors)) {
141 2
            return;
142
        }
143
144
        $code = null;
145
        foreach ($errors as $error) {
146
            /* @var $error Error */
147
            if (null === $code) {
148
                $code = $error->getStatus();
149
            } elseif ($code !== $error->getStatus()) {
150
                $code = 400;
151
                break;
152
            }
153
        }
154
155
        throw new JsonApiException($errors, $code);
156
    }
157
158
    /**
159
     * @inheritdoc
160
     */
161 1
    public function getResponseFactory(RequestInterface $request)
162
    {
163 1
        return new ResponseFactory($this->schemas, $request->getEnvironment(), $request->getQuery());
164
    }
165
166
    /**
167
     * Returns JSON API environment configured in request
168
     *
169
     * @param Request $request
170
     * @return EnvironmentInterface
171
     */
172 6
    public function getRequestEnvironment(Request $request)
173
    {
174 6
        if (false === $request->attributes->has('_jsonapi')) {
175 1
            throw new \RuntimeException('JSON API environment is not provided');
176
        }
177
178 5
        $environment = $request->attributes->get('_jsonapi');
179 5
        if (!$environment instanceof EnvironmentInterface) {
180 1
            throw new \InvalidArgumentException(sprintf(
181 1
                "JSON API environment should implement %s interface",
182 1
                EnvironmentInterface::class
183
            ));
184
        }
185
186 4
        return $environment;
187
    }
188
189
    /**
190
     * Initialize JSON API environment for specified request
191
     *
192
     * @param EnvironmentInterface $environment
193
     * @param Request $request
194
     */
195 4
    private function initializeEnvironment(EnvironmentInterface $environment, Request $request)
196
    {
197 4
        $matcher = $this->createMatcher($environment);
198
199 3
        $this->parseRequestHeaders($request, $matcher);
200
201
        $environment
202 3
            ->setDecoder($matcher->getDecoder())
203 3
            ->setEncoder($matcher->getEncoder())
204 3
            ->setEncoderMediaType($matcher->getEncoderRegisteredMatchedType());
205 3
    }
206
207
    /**
208
     * Create codec matcher for specified environment
209
     *
210
     * @param EnvironmentInterface $environment
211
     * @return \Neomerx\JsonApi\Contracts\Codec\CodecMatcherInterface
212
     */
213 4
    private function createMatcher(EnvironmentInterface $environment)
214
    {
215 4
        $matcher = $this->factory->createCodecMatcher();
216
217 4
        $config = $environment->getMatcherConfiguration();
218 4
        if ((array_key_exists('decoders', $config)) && (is_array($config['decoders']))) {
219 4
            $this->registerDecoders($config['decoders'], $matcher);
220
        }
221
222 3
        if ((array_key_exists('encoders', $config)) && (is_array($config['encoders']))) {
223 3
            $this->registerEncoders($config['encoders'], $matcher);
224
        }
225
226 3
        return $matcher;
227
    }
228
229
    /**
230
     * Convert media type string to media type object
231
     *
232
     * @param string $type
233
     * @return \Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface
234
     */
235 4
    private function parseMediaTypeString($type)
236
    {
237 4
        $parts = explode('/', $type);
238 4
        if (2 !== count($parts)) {
239 1
            throw new \InvalidArgumentException(sprintf("Invalid media type '%s' specified", $type));
240
        }
241
242 3
        return $this->factory->createMediaType($parts[0], $parts[1]);
243
    }
244
245
    /**
246
     * Parse request headers and detect appropriate decoder and encoder
247
     *
248
     * @param Request $request
249
     * @param CodecMatcherInterface $matcher
250
     */
251 3
    private function parseRequestHeaders(Request $request, CodecMatcherInterface $matcher)
252
    {
253 3
        $psr7Request = $this->createPsr7Request($request);
254 3
        $headers = $this->factory->createHeaderParametersParser()->parse($psr7Request);
255 3
        $checker = $this->factory->createHeadersChecker($matcher);
256
257 3
        $checker->checkHeaders($headers);
258 3
    }
259
260
    /**
261
     * Create PSR7 request from symfony http foundation request
262
     *
263
     * @param Request $request
264
     * @return Psr7Request
265
     */
266 3
    private function createPsr7Request(Request $request)
267
    {
268 3
        return new Psr7Request(
269 3
            function () use ($request) {
270 3
                return $request->getMethod();
271 3
            },
272 3
            function ($name) use ($request) {
273 3
                $header = $request->headers->get($name);
274 3
                if (!is_array($header)) {
275 3
                    $header = array($header);
276
                }
277
278 3
                return $header;
279 3
            },
280 3
            function () use ($request) {
281 2
                return $request->query->all();
282 3
            }
283
        );
284
    }
285
286
    /**
287
     * Parse request query parameters
288
     *
289
     * @param Request $request
290
     * @param EnvironmentInterface $environment
291
     * @return \Neomerx\JsonApi\Contracts\Encoder\Parameters\EncodingParametersInterface|null
292
     */
293 3
    private function parseQuery(Request $request, EnvironmentInterface $environment)
294
    {
295 3
        if (null === $environment->getQueryType()) {
296 1
            return null;
297
        }
298
299 2
        $queryParser = $this->factory->createQueryParametersParser();
300 2
        if ($queryParser instanceof QueryParametersParserInterface) {
301
            $queryParser
302 2
                ->setDataParser($this->parser)
303 2
                ->setQueryType($environment->getQueryType());
304
        }
305
306 2
        return $queryParser->parse($this->createPsr7Request($request));
307
    }
308
309
    /**
310
     * Parse request body
311
     *
312
     * @param Request $request
313
     * @param EnvironmentInterface $environment
314
     * @return mixed|null
315
     */
316 3
    private function parseBody(Request $request, EnvironmentInterface $environment)
317
    {
318 3
        if (null === $environment->getBodyType()) {
319 2
            return null;
320
        }
321
322 1
        $decoder = $environment->getDecoder();
323 1
        if ($decoder instanceof DecoderInterface) {
324 1
            $decoder->setContentType($environment->getBodyType());
325
        }
326
327 1
        return $decoder->decode($request->getContent());
328
    }
329
330
    /**
331
     * Validate specified data
332
     *
333
     * @param mixed $data
334
     * @param array|null $validationGroups
335
     * @return Error[]
336
     */
337 2
    private function validateData($data = null, array $validationGroups = null)
338
    {
339 2
        return (null !== $data) ? $this->validator->validate($data, $validationGroups) : [];
340
    }
341
342
    /**
343
     * Register specified decoders
344
     *
345
     * @param array $decoders
346
     * @param CodecMatcherInterface $matcher
347
     */
348 4
    private function registerDecoders(array $decoders, CodecMatcherInterface $matcher)
349
    {
350 4
        foreach ($decoders as $mediaType => $decoderType) {
351 4
            $matcher->registerDecoder(
352 4
                $this->parseMediaTypeString($mediaType),
353 3
                $this->registry->getDecoder($decoderType)
354
            );
355
        }
356 3
    }
357
358
    /**
359
     * Register specified encoders
360
     *
361
     * @param array $encoders
362
     * @param CodecMatcherInterface $matcher
363
     */
364 3
    private function registerEncoders(array $encoders, CodecMatcherInterface $matcher)
365
    {
366 3
        foreach ($encoders as $mediaType => $encoderType) {
367 3
            $matcher->registerEncoder(
368 3
                $this->parseMediaTypeString($mediaType),
369 3
                $this->registry->getEncoder($encoderType)
370
            );
371
        }
372 3
    }
373
}
374