Request   B
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 329
Duplicated Lines 0 %

Test Coverage

Coverage 81.55%

Importance

Changes 0
Metric Value
dl 0
loc 329
ccs 84
cts 103
cp 0.8155
rs 8.8
c 0
b 0
f 0
wmc 36

18 Methods

Rating   Name   Duplication   Size   Complexity  
A normalizeUri() 0 10 2
A validateObjectId() 0 7 3
A normalizeFormData() 0 19 4
A getUri() 0 3 1
A __construct() 0 8 1
A requestPatch() 0 3 1
A requestGet() 0 3 1
A createResponseReflectionInstance() 0 13 2
A disableFormNormalizer() 0 3 1
A requestDelete() 0 3 1
A requestPut() 0 3 1
A validateConfig() 0 5 1
A requestPost() 0 3 1
A setSerializationContextFor() 0 5 1
B applyResponseTransformers() 0 9 5
B sendRequest() 0 34 6
A appendToUri() 0 8 2
A execute() 0 12 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace CCT\Kong\Http;
6
7
use Assert\Assert;
8
use CCT\Kong\Config;
9
use CCT\Kong\Exception\InvalidParameterException;
10
use CCT\Kong\Exception\ServiceUnavailableException;
11
use CCT\Kong\Form\Normalizer\DefaultFormNormalizer;
12
use CCT\Kong\Form\Normalizer\FormNormalizerInterface;
13
use CCT\Kong\Http\Definition\QueryParams;
14
use CCT\Kong\Transformer\TransformerInterface;
15
use GuzzleHttp\Client as GuzzleClient;
16
use GuzzleHttp\Exception\ConnectException;
17
use GuzzleHttp\Exception\RequestException;
18
use GuzzleHttp\Psr7\Uri;
19
use JMS\Serializer\SerializationContext;
20
use JMS\Serializer\Serializer;
21
22
abstract class Request implements RequestInterface
23
{
24
    /**
25
     * @var GuzzleClient
26
     */
27
    protected $client;
28
29
    /**
30
     * @var Serializer
31
     */
32
    protected $serializer;
33
34
    /**
35
     * @var Config
36
     */
37
    protected $config;
38
39
    /**
40
     * @param GuzzleClient  $client
41
     * @param Serializer    $serializer
42
     * @param Config        $config
43
     */
44 46
    public function __construct(GuzzleClient $client, Serializer $serializer, Config $config)
45
    {
46 46
        $this->client = $client;
47 46
        $this->serializer = $serializer;
48 46
        $this->config = $config;
49
50 46
        $this->setUp();
51 46
        $this->validateConfig();
52 46
    }
53
54
    /**
55
     * @return string|null
56
     */
57 22
    public function getUri()
58
    {
59 22
        return $this->config->get(Config::URI_PREFIX);
60
    }
61
62
    /**
63
     * @param string            $uri
64
     * @param QueryParams|null  $queryParams
65
     *
66
     * @return ResponseInterface|\Symfony\Component\HttpFoundation\Response
67
     */
68 27
    protected function requestGet($uri, QueryParams $queryParams = null)
69
    {
70 27
        return $this->execute(self::METHOD_GET, $uri, [], $queryParams);
71
    }
72
73
    /**
74
     * @param string            $uri
75
     * @param QueryParams|null  $queryParams
76
     *
77
     * @return ResponseInterface|\Symfony\Component\HttpFoundation\Response
78
     */
79 4
    protected function requestDelete($uri, QueryParams $queryParams = null)
80
    {
81 4
        return $this->execute(self::METHOD_DELETE, $uri, [], $queryParams);
82
    }
83
84
    /**
85
     * @param string            $uri
86
     * @param array|object      $formData
87
     * @param QueryParams|null  $queryParams
88
     *
89
     * @return ResponseInterface|\Symfony\Component\HttpFoundation\Response
90
     */
91 8
    protected function requestPost($uri, $formData, QueryParams $queryParams = null)
92
    {
93 8
        return $this->execute(self::METHOD_POST, $uri, $formData, $queryParams);
94
    }
95
96
    /**
97
     * @param string            $uri
98
     * @param array|object      $formData
99
     * @param QueryParams|null  $queryParams
100
     *
101
     * @return ResponseInterface|\Symfony\Component\HttpFoundation\Response
102
     */
103 4
    protected function requestPatch($uri, $formData, QueryParams $queryParams = null)
104
    {
105 4
        return $this->execute(self::METHOD_PATCH, $uri, $formData, $queryParams);
106
    }
107
108
    /**
109
     * @param string            $uri
110
     * @param array|object      $formData
111
     * @param QueryParams|null  $queryParams
112
     *
113
     * @return ResponseInterface|\Symfony\Component\HttpFoundation\Response
114
     */
115 4
    protected function requestPut($uri, $formData, QueryParams $queryParams = null)
116
    {
117 4
        return $this->execute(self::METHOD_PUT, $uri, $formData, $queryParams);
118
    }
119
120
    /**
121
     * @param string           $method
122
     * @param string           $uri
123
     * @param array|object     $formData
124
     * @param QueryParams|null $queryParams
125
     *
126
     * @return ResponseInterface|\Symfony\Component\HttpFoundation\Response
127
     */
128 37
    protected function execute($method, string $uri, $formData = [], QueryParams $queryParams = null)
129
    {
130 37
        $queryParams = $queryParams ?: new QueryParams();
131 37
        $formData = $this->normalizeFormData($formData);
0 ignored issues
show
Bug introduced by
It seems like $formData can also be of type object; however, parameter $formData of CCT\Kong\Http\Request::normalizeFormData() does only seem to accept array, 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

131
        $formData = $this->normalizeFormData(/** @scrutinizer ignore-type */ $formData);
Loading history...
132 37
        $uri = $this->normalizeUri($uri, $queryParams);
133
134 37
        $response = $this->sendRequest($method, $uri, $formData);
135 37
        $this->applyResponseTransformers($response);
136
137 37
        $this->config->set('serialization_context', []);
138
139 37
        return $response;
140
    }
141
142
    /**
143
     * @param string $method
144
     * @param string $uri
145
     * @param array  $formData
146
     *
147
     * @throws ServiceUnavailableException
148
     *
149
     * @return Response
150
     */
151 37
    private function sendRequest($method, string $uri, $formData = [])
152
    {
153 37
        $responseRef = $this->createResponseReflectionInstance();
154 37
        $baseUri = $this->client->getConfig('base_uri');
155
156
        // todo: review
157 37
        $uri = ($baseUri instanceof Uri && $baseUri->getPath())
158
            ? rtrim($baseUri->getPath(), '/') . '/' . ltrim($uri, '/')
159 37
            : $uri
160
        ;
161
162
        try {
163 37
            $psrResponse = $this->client->request($method, $uri, $formData);
164
165 37
            $response = $responseRef->newInstance(
166 37
                $psrResponse->getBody()->getContents(),
167 37
                $psrResponse->getStatusCode(),
168 37
                $psrResponse->getHeaders()
169
            );
170
        } catch (ConnectException $e) {
171
            throw new ServiceUnavailableException($e->getRequest(), $e->getMessage());
172
        } catch (RequestException $e) {
173
            if (null === $e->getResponse()->getBody()) {
174
                throw $e;
175
            }
176
177
            $response = $responseRef->newInstance(
178
                $e->getResponse()->getBody()->getContents(),
179
                $e->getResponse()->getStatusCode(),
180
                $e->getResponse()->getHeaders()
181
            );
182
        }
183
184 37
        return $response;
185
    }
186
187
    /**
188
     * Initialization of the request.
189
     *
190
     * @return void
191
     */
192
    protected abstract function setUp();
193
194
    /**
195
     * Appends new parameters to the URI.
196
     *
197
     * @param string        $complement
198
     * @param string|null   $uri
199
     *
200
     * @return string
201
     */
202 21
    protected function appendToUri(string $complement, ?string $uri = null)
203
    {
204 21
        $uri = $uri ?: $this->config->get(Config::URI_PREFIX);
205
206 21
        return sprintf(
207 21
            '%s/%s',
208 21
            rtrim($uri, '/'),
209 21
            ltrim($complement, '/')
210
        );
211
    }
212
213
    /**
214
     * It is possible to handle the Response data defining the Config key response_transformers
215
     * with an instance of Closure or an instance of TransformerInterface.
216
     *
217
     * @param ResponseInterface $data
218
     *
219
     * @return void
220
     */
221 37
    protected function applyResponseTransformers(ResponseInterface $data)
222
    {
223 37
        foreach ($this->config->get(Config::RESPONSE_TRANSFORMERS, []) as $transformer) {
224 35
            if ($transformer instanceof TransformerInterface && $transformer->supports($data)) {
225 24
                $transformer->transform($data);
226
            }
227
228 35
            if ($transformer instanceof \Closure) {
229 35
                $transformer($data);
230
            }
231
        }
232 37
    }
233
234
    /**
235
     * Tries to identify the data object sent, and convert them
236
     * into an array properly handled by the JMSSerializer
237
     * and for the acceptance of Kong API.
238
     *
239
     * @param array $formData
240
     *
241
     * @return array
242
     */
243 37
    private function normalizeFormData($formData = [])
244
    {
245 37
        $defaultFormNormalizer = new DefaultFormNormalizer(
246 37
            $this->serializer,
247 37
            $this->config->get('serialization_context')
248
        );
249
250 37
        $formNormalizer = $this->config->get(Config::FORM_NORMALIZER, $defaultFormNormalizer);
251 37
        if (!$formNormalizer instanceof FormNormalizerInterface) {
252
            return [];
253
        }
254
255 37
        $formParams = $formNormalizer->normalize($formData);
256
257 37
        if (!empty($formParams) && !isset($formParams['form_params'])) {
258 15
            $formParams = ['form_params' => $formParams];
259
        }
260
261 37
        return $formParams;
262
    }
263
264
    /**
265
     * Adds a query string params to the URI.
266
     *
267
     * @param string      $uri
268
     * @param QueryParams $queryParams
269
     *
270
     * @return string
271
     */
272 37
    private function normalizeUri(string $uri, QueryParams $queryParams)
273
    {
274 37
        if (false !== strpos($uri, '?')) {
275
            throw new InvalidParameterException(sprintf(
276
                'It was not possible to normalize the URI as the current URI %s already has the interrogation char in its string.'.
277
                $uri
278
            ));
279
        }
280
281 37
        return $uri . $queryParams->toString();
282
    }
283
284
    /**
285
     * Sets the Serialization context based in the groups the request should deal with.
286
     *
287
     * @param array $groups
288
     *
289
     * @return void
290
     */
291 15
    protected function setSerializationContextFor(array $groups = []): void
292
    {
293 15
        $serializationContext = SerializationContext::create()->setGroups($groups);
294
295 15
        $this->config->set('serialization_context', $serializationContext);
296 15
    }
297
298
    /**
299
     * Creates a Reflection Response class.
300
     *
301
     * @return \ReflectionClass
302
     */
303 37
    private function createResponseReflectionInstance(): \ReflectionClass
304
    {
305 37
        $responseClass = $this->config->get(Config::RESPONSE_CLASS, Response::class);
306 37
        $responseRef = new \ReflectionClass($responseClass);
307
308 37
        if (!$responseRef->implementsInterface(ResponseInterface::class)) {
309
            throw new InvalidParameterException(sprintf(
310
                'The response class must be an implementation of %s',
311
                ResponseInterface::class
312
            ));
313
        }
314
315 37
        return $responseRef;
316
    }
317
318
    protected function disableFormNormalizer()
319
    {
320
        $this->config->set(Config::FORM_NORMALIZER, null);
321
    }
322
323
    /**
324
     * Validates if the object has a valid id, otherwise throws an exception.
325
     *
326
     * @param object $object
327
     *
328
     * @return void
329
     */
330 9
    protected function validateObjectId($object)
331
    {
332 9
        if (!method_exists($object, 'getId') || null === $object->getId()) {
333 2
            throw new InvalidParameterException(sprintf(
334 2
                'The object "%s" must have an ID to continue the operation. "%s" given.',
335 2
                get_class($object),
336 2
                gettype($object->getId())
337
            ));
338
        }
339 7
    }
340
341
    /**
342
     * Validates the required parameters from Config file.
343
     *
344
     * @return void
345
     */
346 46
    private function validateConfig()
347
    {
348 46
        Assert::lazy()
349 46
            ->that($this->config->all(), Config::URI_PREFIX)->keyExists(Config::URI_PREFIX)
350 46
            ->verifyNow()
351
        ;
352 46
    }
353
}
354