Passed
Branch php-scrutinizer (9ddcba)
by Jens
09:45
created

Guzzle5Adapter::executeBatch()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 0
cts 19
cp 0
rs 9.3142
c 0
b 0
f 0
cc 3
eloc 14
nc 3
nop 2
crap 12
1
<?php
2
/**
3
 * @author @jenschude <[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) {
1 ignored issue
show
introduced by
The condition $logger instanceof Psr\Log\LoggerInterface can never be false since $logger is always a sub-type of Psr\Log\LoggerInterface.
Loading history...
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
2 ignored issues
show
Bug introduced by
The type GuzzleHttp\Event\Emitter was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type GuzzleHttp\Event\EmitterInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
98
     */
99
    public function getEmitter()
100
    {
101
        return $this->client->getEmitter();
1 ignored issue
show
Bug Best Practice introduced by
The expression return $this->client->getEmitter() also could return the type GuzzleHttp\Promise\PromiseInterface which is incompatible with the documented return type GuzzleHttp\Event\Emitter...uzzleHttp\Event\Emitter.
Loading history...
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);
130
            $guzzleResponse = $this->client->send($guzzleRequest);
1 ignored issue
show
Bug introduced by
It seems like $guzzleRequest can also be of type GuzzleHttp\Promise\PromiseInterface; however, parameter $request of GuzzleHttp\Client::send() does only seem to accept Psr\Http\Message\RequestInterface, 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

130
            $guzzleResponse = $this->client->send(/** @scrutinizer ignore-type */ $guzzleRequest);
Loading history...
131
            $response = $this->packResponse($guzzleResponse);
132
        } catch (RequestException $exception) {
133
            $response = $this->packResponse($exception->getResponse());
134
            throw ApiException::create($request, $response, $exception);
135
        }
136
137
        return $response;
138
    }
139
140
    protected function packResponse(\GuzzleHttp\Message\ResponseInterface $response = null)
1 ignored issue
show
Bug introduced by
The type GuzzleHttp\Message\ResponseInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
141
    {
142
        if (!$response instanceof \GuzzleHttp\Message\ResponseInterface) {
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
            } else {
179
                $httpResponse = $this->packResponse($result->getResponse());
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(
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(),
1 ignored issue
show
Bug introduced by
The method getUrl() does not exist on Psr\Http\Message\RequestInterface. Did you maybe mean getUri()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

238
                $authRequest->/** @scrutinizer ignore-call */ 
239
                              getUrl(),

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());
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);
264
        $guzzlePromise = $this->client->send($request, $options);
1 ignored issue
show
Bug introduced by
It seems like $request can also be of type GuzzleHttp\Promise\PromiseInterface; however, parameter $request of GuzzleHttp\Client::send() does only seem to accept Psr\Http\Message\RequestInterface, 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

264
        $guzzlePromise = $this->client->send(/** @scrutinizer ignore-type */ $request, $options);
Loading history...
265
266
        $promise = new Guzzle5Promise($guzzlePromise);
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