Passed
Push — develop ( 38ffd9...9f7101 )
by Jens
31:57 queued 05:22
created

Guzzle6Adapter::log()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.5435

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 29
ccs 8
cts 18
cp 0.4444
rs 9.584
c 0
b 0
f 0
cc 3
nc 1
nop 3
crap 4.5435
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\Config;
10
use Commercetools\Core\Helper\CorrelationIdProvider;
11
use Commercetools\Core\Response\AbstractApiResponse;
12
use GuzzleHttp\Client;
13
use GuzzleHttp\ClientInterface;
14
use GuzzleHttp\Exception\GuzzleException;
15
use GuzzleHttp\Exception\RequestException;
16
use GuzzleHttp\Exception\TransferException;
17
use GuzzleHttp\HandlerStack;
18
use GuzzleHttp\MessageFormatter;
19
use GuzzleHttp\Middleware;
20
use GuzzleHttp\Pool;
21
use Psr\Http\Message\RequestInterface;
22
use Psr\Http\Message\ResponseInterface;
23
use Psr\Log\LoggerInterface;
24
use Commercetools\Core\Error\ApiException;
25
use Psr\Log\LogLevel;
26
27
class Guzzle6Adapter implements AdapterOptionInterface, CorrelationIdAware, TokenProviderAware, ConfigAware
28
{
29
    const DEFAULT_CONCURRENCY = 25;
30
    /**
31
     * @var Client
32
     */
33
    protected $client;
34
35
    protected $logger;
36
37
    private $concurrency;
38
39 115
    public function __construct(array $options = [])
40
    {
41 115
        $options = array_merge(
42
            [
43 115
                'allow_redirects' => false,
44
                'verify' => true,
45 115
                'timeout' => 60,
46 115
                'connect_timeout' => 10,
47 115
                'concurrency' => self::DEFAULT_CONCURRENCY
48
            ],
49
            $options
50
        );
51 115
        $this->concurrency = $options['concurrency'];
52 115
        $this->client = new Client($options);
53 115
    }
54
55 44
    public function setLogger(LoggerInterface $logger, $logLevel = LogLevel::INFO, $formatter = null)
56
    {
57 44
        if (is_null($formatter)) {
58 44
            $formatter = new MessageFormatter();
59
        }
60 44
        $this->logger = $logger;
61 44
        $this->addHandler(self::log($logger, $formatter, $logLevel), 'ctp_logger');
62 44
    }
63
64 149
    public function setCorrelationIdProvider(CorrelationIdProvider $provider)
65
    {
66
        $this->addHandler(Middleware::mapRequest(function (RequestInterface $request) use ($provider) {
67 149
            return $request->withAddedHeader(
68 149
                AbstractApiResponse::X_CORRELATION_ID,
69 149
                $provider->getCorrelationId()
70
            );
71 76
        }), 'ctp_correlation_id');
72 76
    }
73
74 138
    public function setOAuthTokenProvider(TokenProvider $tokenProvider)
75
    {
76
        $this->addHandler(Middleware::mapRequest(function (RequestInterface $request) use ($tokenProvider) {
77 138
            return $request->withAddedHeader(
78 138
                'Authorization',
79 138
                'Bearer ' . $tokenProvider->getToken()->getToken()
80
            );
81 61
        }), 'ctp_auth_provider');
82 61
    }
83
84
    /**
85
     * Middleware that logs requests, responses, and errors using a message
86
     * formatter.
87
     *
88
     * @param LoggerInterface  $logger Logs messages.
89
     * @param MessageFormatter $formatter Formatter used to create message strings.
90
     * @param string           $logLevel Level at which to log requests.
91
     *
92
     * @return callable Returns a function that accepts the next handler.
93
     */
94 121
    private static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
95
    {
96
        return function (callable $handler) use ($logger, $formatter, $logLevel) {
97
            return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
98 121
                return $handler($request, $options)->then(
99
                    function ($response) use ($logger, $request, $formatter, $logLevel) {
100 121
                        $message = $formatter->format($request, $response);
101
                        $context = [
102 121
                            AbstractApiResponse::X_CORRELATION_ID => $response->getHeader(
103 121
                                AbstractApiResponse::X_CORRELATION_ID
104
                            )
105
                        ];
106 121
                        $logger->log($logLevel, $message, $context);
107 121
                        return $response;
108 121
                    },
109
                    function ($reason) use ($logger, $request, $formatter) {
110
                        $response = null;
111
                        $context = [];
112
                        if ($reason instanceof RequestException) {
113
                            $response = $reason->getResponse();
114
                            if (!is_null($response)) {
115
                                $context[AbstractApiResponse::X_CORRELATION_ID] = $response->getHeader(
116
                                    AbstractApiResponse::X_CORRELATION_ID
117
                                );
118
                            }
119
                        }
120
                        $message = $formatter->format($request, $response, $reason);
121
                        $logger->notice($message, $context);
122
                        return \GuzzleHttp\Promise\rejection_for($reason);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Promise\rejection_for() has been deprecated: rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead. ( Ignorable by Annotation )

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

122
                        return /** @scrutinizer ignore-deprecated */ \GuzzleHttp\Promise\rejection_for($reason);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
123 121
                    }
124
                );
125 38
            };
126 44
        };
127
    }
128
129 77
    public function addHandler($handler, $name = '')
130
    {
131
        /**
132
         * @var HandlerStack $stack
133
         */
134 77
        $stack = $this->client->getConfig('handler');
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Client::getConfig() has been deprecated: Client::getConfig will be removed in guzzlehttp/guzzle:8.0. ( Ignorable by Annotation )

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

134
        $stack = /** @scrutinizer ignore-deprecated */ $this->client->getConfig('handler');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
135 77
        $stack->push($handler, $name);
136 77
    }
137
138
    /**
139
     * @param RequestInterface $request
140
     * @param array $clientOptions
141
     * @return ResponseInterface
142
     * @throws ApiException
143
     * @throws \Commercetools\Core\Error\ApiException
144
     * @throws \Commercetools\Core\Error\BadGatewayException
145
     * @throws \Commercetools\Core\Error\ConcurrentModificationException
146
     * @throws \Commercetools\Core\Error\ErrorResponseException
147
     * @throws \Commercetools\Core\Error\GatewayTimeoutException
148
     * @throws \Commercetools\Core\Error\InternalServerErrorException
149
     * @throws \Commercetools\Core\Error\InvalidTokenException
150
     * @throws \Commercetools\Core\Error\NotFoundException
151
     * @throws \Commercetools\Core\Error\ServiceUnavailableException
152
     */
153 161
    public function execute(RequestInterface $request, array $clientOptions = [])
154
    {
155
        try {
156 161
            $response = $this->client->send($request, $clientOptions);
157 28
        } catch (RequestException $exception) {
158 27
            $response = $exception->getResponse();
159 27
            throw ApiException::create($request, $response, $exception);
160 1
        } catch (TransferException $exception) {
161 1
            throw ApiException::create($request, null, $exception);
162
        }
163
164 142
        return $response;
165
    }
166
167
    /**
168
     * @param RequestInterface[] $requests
169
     * @param array $clientOptions
170
     * @return ResponseInterface[]
171
     */
172 89
    public function executeBatch(array $requests, array $clientOptions = [])
173
    {
174 89
        $results = Pool::batch(
175 89
            $this->client,
176
            $requests,
177
            [
178 89
                'concurrency' => $this->concurrency,
179 89
                'options' => $clientOptions
180
            ]
181
        );
182
183 89
        $responses = [];
184 89
        foreach ($results as $key => $result) {
185 89
            $httpResponse = $result;
186 89
            if ($result instanceof RequestException) {
187 4
                $request = $requests[$key];
188 4
                $httpResponse = $result->getResponse();
189 4
                $httpResponse = ApiException::create($request, $httpResponse, $result);
190 85
            } elseif ($result instanceof TransferException) {
191 1
                $request = $requests[$key];
192 1
                $httpResponse = ApiException::create($request, null, $result);
193
            }
194
195 89
            $responses[$key] = $httpResponse;
196
        }
197
198 89
        return $responses;
199
    }
200
201
    /**
202
     * @param $oauthUri
203
     * @param $clientId
204
     * @param $clientSecret
205
     * @param $formParams
206
     * @return ResponseInterface
207
     */
208 57
    public function authenticate($oauthUri, $clientId, $clientSecret, $formParams)
209
    {
210
        $options = [
211 57
            'form_params' => $formParams,
212 57
            'auth' => [$clientId, $clientSecret]
213
        ];
214
215
        try {
216 57
            $response = $this->client->post($oauthUri, $options);
217 3
        } catch (RequestException $exception) {
218 3
            throw ApiException::create($exception->getRequest(), $exception->getResponse(), $exception);
219
        }
220 56
        return $response;
221
    }
222
223
    /**
224
     * @param RequestInterface $request
225
     * @param array $clientOptions
226
     * @return AdapterPromiseInterface
227
     */
228 6
    public function executeAsync(RequestInterface $request, array $clientOptions = [])
229
    {
230 6
        $guzzlePromise = $this->client->sendAsync($request, $clientOptions);
231
232 6
        return new Guzzle6Promise($guzzlePromise);
233
    }
234
235 78
    public static function getAdapterInfo()
236
    {
237 78
        if (defined('\GuzzleHttp\Client::MAJOR_VERSION')) {
238 78
            $clientVersion = (string) constant(Client::class . '::MAJOR_VERSION');
239
        } else {
240
            $clientVersion = (string) constant(Client::class . '::VERSION');
241
        }
242 78
        return 'GuzzleHttp/' . $clientVersion;
243
    }
244
245
    /**
246
     * @inheritdoc
247
     */
248 2
    public function getConfig($option)
249
    {
250 2
        return $this->client->getConfig($option);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Client::getConfig() has been deprecated: Client::getConfig will be removed in guzzlehttp/guzzle:8.0. ( Ignorable by Annotation )

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

250
        return /** @scrutinizer ignore-deprecated */ $this->client->getConfig($option);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
251
    }
252
}
253