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 ( d93906...40c4b1 )
by Cees-Jan
01:58
created

Client::getHydrator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace ApiClients\Foundation\Transport;
5
6
use ApiClients\Foundation\Hydrator\Factory as HydratorFactory;
7
use ApiClients\Foundation\Hydrator\Hydrator;
8
use ApiClients\Foundation\Hydrator\Options as HydratorOptions;
9
use GuzzleHttp\Client as GuzzleClient;
10
use GuzzleHttp\Psr7\Request;
11
use GuzzleHttp\Psr7\Response;
12
use Psr\Http\Message\RequestInterface;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\UriInterface;
15
use React\Cache\CacheInterface;
16
use React\EventLoop\LoopInterface;
17
use React\Promise\Deferred;
18
use React\Promise\PromiseInterface;
19
use function React\Promise\reject;
20
use React\Promise\RejectedPromise;
21
use function React\Promise\resolve;
22
use function WyriHaximus\React\futureFunctionPromise;
23
24
class Client
25
{
26
    const DEFAULT_OPTIONS = [
27
        Options::SCHEMA => 'https',
28
        Options::PATH => '/',
29
        Options::USER_AGENT => 'WyriHaximus/php-api-client',
30
        Options::HEADERS => [],
31
    ];
32
33
    /**
34
     * @var GuzzleClient
35
     */
36
    protected $handler;
37
38
    /**
39
     * @var LoopInterface
40
     */
41
    protected $loop;
42
43
    /**
44
     * @var array
45
     */
46
    protected $options = [];
47
48
    /**
49
     * @var Hydrator
50
     */
51
    protected $hydrator;
52
53
    /**
54
     * @var CacheInterface
55
     */
56
    protected $cache;
57
58
    /**
59
     * @param LoopInterface $loop
60
     * @param GuzzleClient $handler
61
     * @param array $options
62
     */
63 12
    public function __construct(LoopInterface $loop, GuzzleClient $handler, array $options = [])
64
    {
65 12
        $this->loop = $loop;
66 12
        $this->handler = $handler;
67 12
        $this->options = $options + self::DEFAULT_OPTIONS;
68 12 View Code Duplication
        if (isset($this->options[Options::CACHE]) && $this->options[Options::CACHE] instanceof CacheInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69 3
            $this->cache = $this->options[Options::CACHE];
70
        }
71 12
        $this->hydrator = $this->determineHydrator();
72 12
    }
73
74
    /**
75
     * @return Hydrator
76
     * @throws \Exception
77
     */
78 12
    protected function determineHydrator(): Hydrator
79
    {
80 12 View Code Duplication
        if (isset($this->options[Options::HYDRATOR]) && $this->options[Options::HYDRATOR] instanceof Hydrator) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
81
            return $this->options[Options::HYDRATOR];
82
        }
83
84 12
        if (!isset($this->options[Options::HYDRATOR_OPTIONS])) {
85
            throw new \Exception('Missing Hydrator options');
86
        }
87
88 12
        if (!isset($this->options[Options::HYDRATOR_OPTIONS][HydratorOptions::EXTRA_PROPERTIES])) {
89 12
            $this->options[Options::HYDRATOR_OPTIONS][HydratorOptions::EXTRA_PROPERTIES] = [];
90
        }
91
92 12
        $this->options[Options::HYDRATOR_OPTIONS][HydratorOptions::EXTRA_PROPERTIES]['setTransport'] = $this;
93
94 12
        return HydratorFactory::create($this->options[Options::HYDRATOR_OPTIONS]);
95
    }
96
97
    /**
98
     * @param string $path
99
     * @param bool $refresh
100
     * @return PromiseInterface
101
     */
102 5
    public function request(string $path, bool $refresh = false): PromiseInterface
103
    {
104
        return $this->requestRaw($path, $refresh)->then(function ($json) {
105 3
            return $this->jsonDecode($json);
106 5
        });
107
    }
108
109
    /**
110
     * @param string $path
111
     * @param bool $refresh
112
     * @return PromiseInterface
113
     */
114 5
    public function requestRaw(string $path, bool $refresh = false): PromiseInterface
115
    {
116 5
        return $this->requestPsr7(
117 5
            $this->createRequest($path),
118
            $refresh
119
        )->then(function ($response) {
120 3
            return resolve($response->getBody()->getContents());
121 5
        });
122
    }
123
124
    /**
125
     * @param UriInterface $uri
126
     * @return PromiseInterface
127
     */
128 3
    protected function checkCache(UriInterface $uri): PromiseInterface
129
    {
130 3
        if (!($this->cache instanceof CacheInterface)) {
131 1
            return reject();
132
        }
133
134 2
        $key = $this->determineCacheKey($uri);
135
        return $this->cache->get($key)->then(function ($document) {
136 1
            $document = json_decode($document, true);
137 1
            $response = new Response(
138 1
                $document['status_code'],
139 1
                $document['headers'],
140 1
                $document['body'],
141 1
                $document['protocol_version'],
142 1
                $document['reason_phrase']
143
            );
144
145 1
            return resolve($response);
146 2
        });
147
    }
148
149
    /**
150
     * @param RequestInterface $request
151
     * @param ResponseInterface $response
152
     */
153 2
    protected function storeCache(RequestInterface $request, ResponseInterface $response)
154
    {
155 2
        if (!($this->cache instanceof CacheInterface)) {
156
            return;
157
        }
158
159
        $document = [
160 2
            'body' => $response->getBody()->getContents(),
161 2
            'headers' => $response->getHeaders(),
162 2
            'protocol_version' => $response->getProtocolVersion(),
163 2
            'reason_phrase' => $response->getReasonPhrase(),
164 2
            'status_code' => $response->getStatusCode(),
165
        ];
166
167 2
        $key = $this->determineCacheKey($request->getUri());
168
169 2
        $this->cache->set($key, json_encode($document));
170 2
    }
171
172
    /**
173
     * @param UriInterface $uri
174
     * @return string
175
     */
176 3
    protected function determineCacheKey(UriInterface $uri): string
177
    {
178 3
        return $this->stripExtraSlashes(
179
            implode(
180 3
                '/',
181
                [
182 3
                    $uri->getScheme(),
183 3
                    $uri->getHost(),
184 3
                    $uri->getPort(),
185 3
                    $uri->getPath(),
186 3
                    md5($uri->getQuery()),
187
                ]
188
            )
189
        );
190
    }
191
192
    /**
193
     * @param string $string
194
     * @return string
195
     */
196 3
    protected function stripExtraSlashes(string $string): string
197
    {
198 3
        return preg_replace('#/+#', '/', $string);
199
    }
200
201
    /**
202
     * @param RequestInterface $request
203
     * @param bool $refresh
204
     * @return PromiseInterface
205
     */
206 5
    public function requestPsr7(RequestInterface $request, bool $refresh = false): PromiseInterface
207
    {
208 5
        $promise = new RejectedPromise();
209
210 5
        if (!$refresh) {
211 3
            $promise = $this->checkCache($request->getUri());
212
        }
213
214
        return $promise->otherwise(function () use ($request) {
215 4
            $deferred = new Deferred();
216
217 4
            $this->handler->sendAsync(
218
                $request
219
            )->then(function (ResponseInterface $response) use ($deferred, $request) {
220 2
                $contents = $response->getBody()->getContents();
221 2
                $this->storeCache(
222
                    $request,
223 2
                    new Response(
224 2
                        $response->getStatusCode(),
225 2
                        $response->getHeaders(),
226
                        $contents,
227 2
                        $response->getProtocolVersion(),
228 2
                        $response->getReasonPhrase()
229
                    )
230
                );
231 2
                $deferred->resolve(
232 2
                    new Response(
233 2
                        $response->getStatusCode(),
234 2
                        $response->getHeaders(),
235
                        $contents,
236 2
                        $response->getProtocolVersion(),
237 2
                        $response->getReasonPhrase()
238
                    )
239
                );
240
            }, function ($error) use ($deferred) {
241
                $deferred->reject($error);
242 4
            });
243
244 4
            return $deferred->promise();
245 5
        });
246
    }
247
248
    /**
249
     * @param string $path
250
     * @return RequestInterface
251
     */
252 5
    protected function createRequest(string $path): RequestInterface
253
    {
254 5
        $url = $this->getBaseURL() . $path;
255 5
        $headers = $this->getHeaders();
256 5
        return new Request('GET', $url, $headers);
257
    }
258
259
    /**
260
     * @return array
261
     */
262 5
    public function getHeaders(): array
263
    {
264
        $headers = [
265 5
            'User-Agent' => $this->options[Options::USER_AGENT],
266
        ];
267 5
        $headers += $this->options[Options::HEADERS];
268 5
        return $headers;
269
    }
270
271
    /**
272
     * @param string $json
273
     * @return PromiseInterface
274
     */
275
    public function jsonDecode(string $json): PromiseInterface
276
    {
277 3
        return futureFunctionPromise($this->loop, $json, function ($json) {
278 3
            return json_decode($json, true);
279 3
        });
280
    }
281
282
    /**
283
     * @return Hydrator
284
     */
285 1
    public function getHydrator(): Hydrator
286
    {
287 1
        return $this->hydrator;
288
    }
289
290
    /**
291
     * @return LoopInterface
292
     */
293 3
    public function getLoop(): LoopInterface
294
    {
295 3
        return $this->loop;
296
    }
297
298
    /**
299
     * @return string
300
     */
301 8
    public function getBaseURL(): string
302
    {
303 8
        return $this->options[Options::SCHEMA] . '://' . $this->options[Options::HOST] . $this->options[Options::PATH];
304
    }
305
}
306