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

Guzzle6Adapter::executeBatch()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 15

Duplication

Lines 5
Ratio 20.83 %

Code Coverage

Tests 15
CRAP Score 3

Importance

Changes 0
Metric Value
dl 5
loc 24
ccs 15
cts 15
cp 1
rs 8.9713
c 0
b 0
f 0
cc 3
eloc 15
nc 3
nop 2
crap 3
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\Response\AbstractApiResponse;
11
use GuzzleHttp\Client;
12
use GuzzleHttp\Exception\RequestException;
13
use GuzzleHttp\MessageFormatter;
14
use GuzzleHttp\Middleware;
15
use GuzzleHttp\Pool;
16
use Psr\Http\Message\RequestInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Log\LoggerInterface;
19
use Commercetools\Core\Error\ApiException;
20
use Psr\Log\LogLevel;
21
22
class Guzzle6Adapter implements AdapterOptionInterface, CorrelationIdAware, TokenProviderAware
23
{
24
    const DEFAULT_CONCURRENCY = 25;
25
    /**
26
     * @var Client
27
     */
28
    protected $client;
29
30
    protected $logger;
31
32
    private $concurrency;
33
34 112
    public function __construct(array $options = [])
35
    {
36 112
        $options = array_merge(
37
            [
38 112
                'allow_redirects' => false,
39
                'verify' => true,
40 112
                'timeout' => 60,
41 112
                'connect_timeout' => 10,
42 112
                'concurrency' => self::DEFAULT_CONCURRENCY
43
            ],
44 112
            $options
45
        );
46 112
        $this->concurrency = $options['concurrency'];
47 112
        $this->client = new Client($options);
48 112
    }
49
50 44
    public function setLogger(LoggerInterface $logger, $logLevel = LogLevel::INFO, $formatter = null)
51
    {
52 44
        if (is_null($formatter)) {
53 44
            $formatter = new MessageFormatter();
54
        }
55 44
        $this->logger = $logger;
56 44
        $this->addHandler(self::log($logger, $formatter, $logLevel));
57 44
    }
58
59
    public function setCorrelationIdProvider(CorrelationIdProvider $provider)
60
    {
61 477
        $this->addHandler(Middleware::mapRequest(function (RequestInterface $request) use ($provider) {
62 477
            return $request->withAddedHeader(
63 477
                AbstractApiResponse::X_CORRELATION_ID,
64 477
                $provider->getCorrelationId()
65
            );
66 39
        }));
67 39
    }
68
69
    public function setOAuthTokenProvider(TokenProvider $tokenProvider)
70
    {
71 499
        $this->addHandler(Middleware::mapRequest(function (RequestInterface $request) use ($tokenProvider) {
72 499
            return $request->withAddedHeader(
73 499
                'Authorization',
74 499
                'Bearer ' . $tokenProvider->getToken()->getToken()
75
            );
76 59
        }));
77 59
    }
78
79
    /**
80
     * Middleware that logs requests, responses, and errors using a message
81
     * formatter.
82
     *
83
     * @param LoggerInterface  $logger Logs messages.
84
     * @param MessageFormatter $formatter Formatter used to create message strings.
85
     * @param string           $logLevel Level at which to log requests.
86
     *
87
     * @return callable Returns a function that accepts the next handler.
88
     */
89
    private static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
90
    {
91
        return function (callable $handler) use ($logger, $formatter, $logLevel) {
92 483
            return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
93 483
                return $handler($request, $options)->then(
94 483
                    function ($response) use ($logger, $request, $formatter, $logLevel) {
95 483
                        $message = $formatter->format($request, $response);
96 483
                        $context[AbstractApiResponse::X_CORRELATION_ID] = $response->getHeader(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$context was never initialized. Although not strictly required by PHP, it is generally a good practice to add $context = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
97 483
                            AbstractApiResponse::X_CORRELATION_ID
98
                        );
99 483
                        $logger->log($logLevel, $message, $context);
100 483
                        return $response;
101 483
                    },
102 483
                    function ($reason) use ($logger, $request, $formatter) {
103
                        $response = null;
104
                        $context = [];
105
                        if ($reason instanceof RequestException) {
106
                            $response = $reason->getResponse();
107
                            if (!is_null($response)) {
108
                                $context[AbstractApiResponse::X_CORRELATION_ID] = $response->getHeader(
109
                                    AbstractApiResponse::X_CORRELATION_ID
110
                                );
111
                            }
112
                        }
113
                        $message = $formatter->format($request, $response, $reason);
114
                        $logger->notice($message, $context);
115
                        return \GuzzleHttp\Promise\rejection_for($reason);
116 483
                    }
117
                );
118 39
            };
119 44
        };
120
    }
121
122 66
    public function addHandler($handler)
123
    {
124 66
        $this->client->getConfig('handler')->push($handler);
125 66
    }
126
127
    /**
128
     * @param RequestInterface $request
129
     * @param array $clientOptions
130
     * @return ResponseInterface
131
     * @throws ApiException
132
     * @throws \Commercetools\Core\Error\ApiException
133
     * @throws \Commercetools\Core\Error\BadGatewayException
134
     * @throws \Commercetools\Core\Error\ConcurrentModificationException
135
     * @throws \Commercetools\Core\Error\ErrorResponseException
136
     * @throws \Commercetools\Core\Error\GatewayTimeoutException
137
     * @throws \Commercetools\Core\Error\InternalServerErrorException
138
     * @throws \Commercetools\Core\Error\InvalidTokenException
139
     * @throws \Commercetools\Core\Error\NotFoundException
140
     * @throws \Commercetools\Core\Error\ServiceUnavailableException
141
     */
142 527
    public function execute(RequestInterface $request, array $clientOptions = [])
143
    {
144
        try {
145 527
            $response = $this->client->send($request, $clientOptions);
146 111
        } catch (RequestException $exception) {
147 111
            $response = $exception->getResponse();
148 111
            throw ApiException::create($request, $response, $exception);
149
        }
150
151 501
        return $response;
152
    }
153
154
    /**
155
     * @param RequestInterface[] $requests
156
     * @param array $clientOptions
157
     * @return ResponseInterface[]
158
     */
159 406
    public function executeBatch(array $requests, array $clientOptions = [])
160
    {
161 406
        $results = Pool::batch(
162 406
            $this->client,
163 406
            $requests,
164
            [
165 406
                'concurrency' => $this->concurrency,
166 406
                'options' => $clientOptions
167
            ]
168
        );
169
170 406
        $responses = [];
171 406
        foreach ($results as $key => $result) {
172 406
            $httpResponse = $result;
173 406 View Code Duplication
            if ($result instanceof RequestException) {
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...
174 16
                $request = $requests[$key];
175 16
                $httpResponse = $result->getResponse();
176 16
                $httpResponse = ApiException::create($request, $httpResponse, $result);
177
            }
178 406
            $responses[$key] = $httpResponse;
179
        }
180
181 406
        return $responses;
182
    }
183
184
    /**
185
     * @param $oauthUri
186
     * @param $clientId
187
     * @param $clientSecret
188
     * @param $formParams
189
     * @return ResponseInterface
190
     */
191 45
    public function authenticate($oauthUri, $clientId, $clientSecret, $formParams)
192
    {
193
        $options = [
194 45
            'form_params' => $formParams,
195 45
            'auth' => [$clientId, $clientSecret]
196
        ];
197
198
        try {
199 45
            $response = $this->client->post($oauthUri, $options);
200 3
        } catch (RequestException $exception) {
201 3
            throw ApiException::create($exception->getRequest(), $exception->getResponse(), $exception);
202
        }
203 44
        return $response;
204
    }
205
206
    /**
207
     * @param RequestInterface $request
208
     * @param array $clientOptions
209
     * @return AdapterPromiseInterface
210
     */
211 4
    public function executeAsync(RequestInterface $request, array $clientOptions = [])
212
    {
213 4
        $guzzlePromise = $this->client->sendAsync($request, $clientOptions);
214
215 4
        return new Guzzle6Promise($guzzlePromise);
216
    }
217
218 75
    public static function getAdapterInfo()
219
    {
220 75
        return 'GuzzleHttp/' . Client::VERSION;
221
    }
222
}
223