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); |
130
|
|
|
$guzzleResponse = $this->client->send($guzzleRequest); |
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) |
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
|
|
View Code Duplication |
} 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(), |
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); |
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
|
|
|
|
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.