Passed
Push — develop ( 58ad97...afcaed )
by Jens
12:59 queued 04:03
created

Guzzle5Adapter::setOAuthTokenProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * @author @jayS-de <[email protected]>
4
 */
5
6
namespace Commercetools\Core\Client\Adapter;
7
8
use Commercetools\Core\Client\OAuth\TokenProvider;
9
use Commercetools\Core\Helper\CorrelationIdProvider;
10
use Commercetools\Core\Helper\Subscriber\CorrelationIdSubscriber;
11
use Commercetools\Core\Helper\Subscriber\TokenSubscriber;
12
use GuzzleHttp\Client;
13
use GuzzleHttp\Exception\RequestException;
14
use GuzzleHttp\Pool;
15
use GuzzleHttp\Psr7\Request;
16
use GuzzleHttp\Psr7\Response;
17
use Psr\Http\Message\RequestInterface;
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Log\LoggerInterface;
20
use Commercetools\Core\Helper\Subscriber\Log\LogSubscriber;
21
use Commercetools\Core\Error\ApiException;
22
use Psr\Log\LogLevel;
23
24
class Guzzle5Adapter implements AdapterOptionInterface, CorrelationIdAware, TokenProviderAware
25
{
26
    const DEFAULT_CONCURRENCY = 25;
27
28
    /**
29
     * @var Client
30
     */
31
    protected $client;
32
33
    /**
34
     * @var LoggerInterface
35
     */
36
    protected $logger;
37
38
    private $concurrency;
39
40
    /**
41
     * @param array $options
42
     */
43
    public function __construct(array $options = [])
44
    {
45
        if (isset($options['base_uri'])) {
46
            $options['base_url'] = $options['base_uri'];
47
            unset($options['base_uri']);
48
        }
49
        if (isset($options['concurrency'])) {
50
            $options['pool_size'] = $options['concurrency'];
51
            unset($options['concurrency']);
52
        }
53
        if (isset($options['headers'])) {
54
            $options['defaults']['headers'] = $options['headers'];
55
            unset($options['headers']);
56
        }
57
        $options = array_merge(
58
            [
59
                'allow_redirects' => false,
60
                'verify' => true,
61
                'timeout' => 60,
62
                'connect_timeout' => 10,
63
                'pool_size' => self::DEFAULT_CONCURRENCY
64
            ],
65
            $options
66
        );
67
        $this->concurrency = $options['pool_size'];
68
69
        $this->client = new Client($options);
70
    }
71
72
    public function setLogger(LoggerInterface $logger, $logLevel = LogLevel::INFO, $formatter = null)
73
    {
74
        $this->logger = $logger;
75
        if ($logger instanceof LoggerInterface) {
76
            $this->getEmitter()->attach(new LogSubscriber($logger, $formatter, $logLevel));
77
        }
78
    }
79
80
    public function addHandler($handler)
81
    {
82
        $this->getEmitter()->attach($handler);
83
    }
84
85
    public function setCorrelationIdProvider(CorrelationIdProvider $provider)
86
    {
87
        $this->addHandler(new CorrelationIdSubscriber($provider));
88
    }
89
90
    public function setOAuthTokenProvider(TokenProvider $tokenProvider)
91
    {
92
        $this->addHandler(new TokenSubscriber($tokenProvider));
93
    }
94
95
    /**
96
     * @internal
97
     * @return \GuzzleHttp\Event\Emitter|\GuzzleHttp\Event\EmitterInterface
98
     */
99
    public function getEmitter()
100
    {
101
        return $this->client->getEmitter();
102
    }
103
104
    /**
105
     * @param RequestInterface $request
106
     * @param array $clientOptions
107
     * @return ResponseInterface
108
     * @throws \Commercetools\Core\Error\ApiException
109
     * @throws \Commercetools\Core\Error\BadGatewayException
110
     * @throws \Commercetools\Core\Error\ConcurrentModificationException
111
     * @throws \Commercetools\Core\Error\ErrorResponseException
112
     * @throws \Commercetools\Core\Error\GatewayTimeoutException
113
     * @throws \Commercetools\Core\Error\InternalServerErrorException
114
     * @throws \Commercetools\Core\Error\InvalidTokenException
115
     * @throws \Commercetools\Core\Error\NotFoundException
116
     * @throws \Commercetools\Core\Error\ServiceUnavailableException
117
     */
118
    public function execute(RequestInterface $request, array $clientOptions = [])
119
    {
120
        $options = [
121
            'headers' => $request->getHeaders(),
122
            'body' => (string)$request->getBody()
123
        ];
124
        if (count($clientOptions)) {
125
            $options = array_merge($options, $clientOptions);
126
        }
127
128
        try {
129
            $guzzleRequest = $this->client->createRequest($request->getMethod(), (string)$request->getUri(), $options);
0 ignored issues
show
Bug introduced by
The method createRequest() does not exist on GuzzleHttp\Client. Did you maybe mean request()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
130
            $guzzleResponse = $this->client->send($guzzleRequest);
131
            $response = $this->packResponse($guzzleResponse);
0 ignored issues
show
Documentation introduced by
$guzzleResponse is of type object<Psr\Http\Message\ResponseInterface>, but the function expects a null|object<GuzzleHttp\Message\ResponseInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
132
        } catch (RequestException $exception) {
133
            $response = $this->packResponse($exception->getResponse());
0 ignored issues
show
Bug introduced by
It seems like $exception->getResponse() targeting GuzzleHttp\Exception\Req...xception::getResponse() can also be of type object<Psr\Http\Message\ResponseInterface>; however, Commercetools\Core\Clien...Adapter::packResponse() does only seem to accept null|object<GuzzleHttp\Message\ResponseInterface>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
134
            throw ApiException::create($request, $response, $exception);
135
        }
136
137
        return $response;
138
    }
139
140
    protected function packResponse(\GuzzleHttp\Message\ResponseInterface $response = null)
141
    {
142
        if (!$response instanceof \GuzzleHttp\Message\ResponseInterface) {
0 ignored issues
show
Bug introduced by
The class GuzzleHttp\Message\ResponseInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
143
            return null;
144
        }
145
        return new Response(
146
            $response->getStatusCode(),
147
            $response->getHeaders(),
148
            (string)$response->getBody()
149
        );
150
    }
151
152
    /**
153
     * @param RequestInterface[] $requests
154
     * @param array $clientOptions
155
     * @return \Psr\Http\Message\ResponseInterface[]
156
     * @throws \Commercetools\Core\Error\ApiException
157
     * @throws \Commercetools\Core\Error\BadGatewayException
158
     * @throws \Commercetools\Core\Error\ConcurrentModificationException
159
     * @throws \Commercetools\Core\Error\ErrorResponseException
160
     * @throws \Commercetools\Core\Error\GatewayTimeoutException
161
     * @throws \Commercetools\Core\Error\InternalServerErrorException
162
     * @throws \Commercetools\Core\Error\InvalidTokenException
163
     * @throws \Commercetools\Core\Error\NotFoundException
164
     * @throws \Commercetools\Core\Error\ServiceUnavailableException
165
     */
166
    public function executeBatch(array $requests, array $clientOptions = [])
167
    {
168
        $results = Pool::batch(
169
            $this->client,
170
            $this->getBatchHttpRequests($requests, $clientOptions),
171
            ['pool_size' => $this->concurrency]
172
        );
173
174
        $responses = [];
175
        foreach ($results as $key => $result) {
176
            if (!$result instanceof RequestException) {
177
                $response = $this->packResponse($result);
178 View Code Duplication
            } else {
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...
179
                $httpResponse = $this->packResponse($result->getResponse());
0 ignored issues
show
Bug introduced by
It seems like $result->getResponse() targeting GuzzleHttp\Exception\Req...xception::getResponse() can also be of type object<Psr\Http\Message\ResponseInterface>; however, Commercetools\Core\Clien...Adapter::packResponse() does only seem to accept null|object<GuzzleHttp\Message\ResponseInterface>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
180
                $request = $requests[$key];
181
                $response = ApiException::create($request, $httpResponse, $result);
182
            }
183
            $responses[$key] = $response;
184
        }
185
186
        return $responses;
187
    }
188
189
    /**
190
     * @param array $requests
191
     * @param array $clientOptions
192
     * @return array
193
     */
194
    protected function getBatchHttpRequests(array $requests, array $clientOptions = [])
195
    {
196
        $requests = array_map(
197
            function ($request) use ($clientOptions) {
198
                $options = ['headers' => $request->getHeaders()];
199
                if (count($clientOptions)) {
200
                    $options = array_merge($options, $clientOptions);
201
                }
202
203
                /**
204
                 * @var RequestInterface $request
205
                 */
206
                return $this->client->createRequest(
0 ignored issues
show
Bug introduced by
The method createRequest() does not exist on GuzzleHttp\Client. Did you maybe mean request()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
207
                    $request->getMethod(),
208
                    (string)$request->getUri(),
209
                    $options
210
                );
211
            },
212
            $requests
213
        );
214
215
        return $requests;
216
    }
217
218
    /**
219
     * @param $oauthUri
220
     * @param $clientId
221
     * @param $clientSecret
222
     * @param $formParams
223
     * @return ResponseInterface
224
     */
225
    public function authenticate($oauthUri, $clientId, $clientSecret, $formParams)
226
    {
227
        $options = [
228
            'body' => $formParams,
229
            'auth' => [$clientId, $clientSecret]
230
        ];
231
232
        try {
233
            $response = $this->client->post($oauthUri, $options);
234
        } catch (RequestException $exception) {
235
            $authRequest = $exception->getRequest();
236
            $request = new Request(
237
                $authRequest->getMethod(),
238
                $authRequest->getUrl(),
0 ignored issues
show
Bug introduced by
The method getUrl() does not seem to exist on object<Psr\Http\Message\RequestInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
239
                $authRequest->getHeaders(),
240
                (string)$authRequest->getBody()
241
            );
242
            $response = $this->packResponse($exception->getResponse());
0 ignored issues
show
Bug introduced by
It seems like $exception->getResponse() targeting GuzzleHttp\Exception\Req...xception::getResponse() can also be of type object<Psr\Http\Message\ResponseInterface>; however, Commercetools\Core\Clien...Adapter::packResponse() does only seem to accept null|object<GuzzleHttp\Message\ResponseInterface>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
243
            throw ApiException::create($request, $response, $exception);
244
        }
245
        return $response;
246
    }
247
248
    /**
249
     * @param RequestInterface $request
250
     * @param array $clientOptions
251
     * @return AdapterPromiseInterface
252
     */
253
    public function executeAsync(RequestInterface $request, array $clientOptions = [])
254
    {
255
        $options = [
256
            'future' => true,
257
            'exceptions' => false,
258
            'headers' => $request->getHeaders()
259
        ];
260
        if (count($clientOptions)) {
261
            $options = array_merge($options, $clientOptions);
262
        }
263
        $request = $this->client->createRequest($request->getMethod(), (string)$request->getUri(), $options);
0 ignored issues
show
Bug introduced by
The method createRequest() does not exist on GuzzleHttp\Client. Did you maybe mean request()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
264
        $guzzlePromise = $this->client->send($request, $options);
265
266
        $promise = new Guzzle5Promise($guzzlePromise);
0 ignored issues
show
Documentation introduced by
$guzzlePromise is of type object<Psr\Http\Message\ResponseInterface>, but the function expects a object<GuzzleHttp\Message\FutureResponse>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
267
        $promise->then(
268
            function (\GuzzleHttp\Message\ResponseInterface $response) {
269
                return new Response(
270
                    $response->getStatusCode(),
271
                    $response->getHeaders(),
272
                    (string)$response->getBody(),
273
                    $response->getProtocolVersion(),
274
                    $response->getReasonPhrase()
275
                );
276
            }
277
        );
278
279
        return $promise;
280
    }
281
282
    public static function getAdapterInfo()
283
    {
284
        return 'GuzzleHttp/' . Client::VERSION;
285
    }
286
}
287