Completed
Push — develop ( 046464...9a0cd6 )
by Jens
13:20
created

Client::getBaseUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * @author @jayS-de <[email protected]>
4
 * @created 19.01.15, 14:29
5
 */
6
7
namespace Commercetools\Core;
8
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Log\LoggerInterface;
12
use Commercetools\Core\Client\Adapter\AdapterInterface;
13
use Commercetools\Core\Error\InvalidTokenException;
14
use Commercetools\Core\Error\Message;
15
use Commercetools\Core\Error\ApiException;
16
use Commercetools\Core\Model\Common\ContextAwareInterface;
17
use Commercetools\Core\Response\ApiResponseInterface;
18
use Commercetools\Core\Request\ClientRequestInterface;
19
use Commercetools\Core\Client\OAuth\Manager;
20
21
/**
22
 * The client for communicating with the commercetools platform
23
 *
24
 * @description
25
 * ## Instantiation
26
 *
27
 * ```php
28
 * $config = Config::fromArray(
29
 *  ['client_id' => '<client_id>', 'client_secret' => '<client_secret>', 'project' => '<project>']
30
 * );
31
 * $client = Client::ofConfig($config);
32
 * ```
33
 *
34
 * ## Execution
35
 *
36
 * ### Synchronous
37
 * There are two main approaches for retrieving result objects
38
 *
39
 * Client centric:
40
 *
41
 * ```php
42
 * $response = $client->execute(ProductProjectionSearchRequest::of());
43
 * $products = $response->toObject();
44
 * ```
45
 *
46
 * Request centric:
47
 *
48
 * ```php
49
 * $request = ProductProjectionSearchRequest::of();
50
 * $response = $request->executeWithClient($client);
51
 * $products = $request->mapResponse($response);
52
 * ```
53
 *
54
 * By using the request centric approach the IDE is capable of resolving the correct classes and give
55
 * a maximum of support with available methods.
56
 *
57
 * ### Asynchronous
58
 * The asynchronous execution will return a promise to fulfill the request.
59
 *
60
 * ```php
61
 * $response = $client->executeAsync(ProductProjectionSearchRequest::of());
62
 * ```
63
 *
64
 * ### Batch
65
 * By filling the batch queue and starting the execution all requests will be executed in parallel.
66
 *
67
 * ```php
68
 * $client->addBatchRequest(ProductProjectionSearchRequest::of())
69
 *     ->addBatchRequest(CartByIdGetRequest::ofId($cartId));
70
 * $responses = $client->executeBatch();
71
 * ```
72
 *
73
 * ## Instantiation options
74
 *
75
 * ### Using a logger
76
 *
77
 * The client uses the PSR-3 logger interface for logging requests and deprecation notices. To enable
78
 * logging provide a PSR-3 compliant logger (e.g. Monolog).
79
 *
80
 * ```php
81
 * $logger = new \Monolog\Logger('name');
82
 * $logger->pushHandler(new StreamHandler('./requests.log'));
83
 * $client = Client::ofConfigAndLogger($config, $logger);
84
 * ```
85
 *
86
 * ### Using a cache adapter ###
87
 *
88
 * The client will automatically request an OAuth token and store the token in the APCu cache, which should be the
89
 * fastest option as there is no network or socket interface used.
90
 *
91
 * It's also possible to use a different cache adapter. The SDK provides a Doctrine, a Redis and an APCu cache adapter.
92
 * By default the SDK tries to instantiate the APCu cache adapter if there is no cache given. E.g. Redis:
93
 *
94
 * ```php
95
 * $redis = new \Redis();
96
 * $redis->connect('localhost');
97
 * $client = Client::ofConfigAndCache($config, $redis);
98
 * ```
99
 *
100
 * #### Using cache and logger ####
101
 *
102
 * ```php
103
 * $client = Client::ofConfigCacheAndLogger($config, $cache, $logger);
104
 * ```
105
 *
106
 * #### Using a custom cache adapter ####
107
 *
108
 * ```php
109
 * class <CacheClass>Adapter implements \Commercetools\Core\Cache\CacheAdapterInterface {
110
 *     protected $cache;
111
 *     public function __construct(<CacheClass> $cache) {
112
 *         $this->cache = $cache;
113
 *     }
114
 * }
115
 *
116
 * $client->getAdapterFactory()->registerCallback(function ($cache) {
117
 *     if ($cache instanceof <CacheClass>) {
118
 *         return new <CacheClass>Adapter($cache);
119
 *     }
120
 *     return null;
121
 * });
122
 * ```
123
 *
124
 * @package Commercetools\Core
125
 */
126
class Client extends AbstractHttpClient
127
{
128
    const DEPRECATION_HEADER = 'X-DEPRECATION-NOTICE';
129
130
    /**
131
     * @sideeffect Test123
132
     * @var LoggerInterface
133
     */
134
    protected $logger;
135
136
    /**
137
     * @var Manager
138
     */
139
    protected $oauthManager;
140
141
    /**
142
     * @var ClientRequestInterface[]
143
     */
144
    protected $batchRequests = [];
145
146
    protected $tokenRefreshed = false;
147
148
    /**
149
     * @param array|Config $config
150
     * @param $cache
151
     * @param LoggerInterface $logger
152
     */
153 83
    public function __construct($config, $cache = null, LoggerInterface $logger = null)
154
    {
155 83
        parent::__construct($config);
156
157 83
        $manager = new Manager($config, $cache);
158 83
        $this->setOauthManager($manager);
159 83
        $this->setLogger($logger);
160 83
    }
161
162
    /**
163
     * @return Manager
164
     */
165 62
    public function getOauthManager()
166
    {
167 62
        return $this->oauthManager;
168
    }
169
170
    /**
171
     * @param Manager $oauthManager
172
     * @return $this
173
     */
174 83
    protected function setOauthManager(Manager $oauthManager)
175
    {
176 83
        $this->oauthManager = $oauthManager;
177 83
        return $this;
178
    }
179
180
    /**
181
     * @param LoggerInterface $logger
182
     * @return $this
183
     */
184 83
    protected function setLogger(LoggerInterface $logger = null)
185
    {
186 83
        if ($logger instanceof LoggerInterface) {
187 4
            $this->logger = $logger;
188 4
        }
189 83
        return $this;
190
    }
191
192
    /**
193
     * @param array $options
194
     * @return AdapterInterface
195
     */
196 81
    public function getHttpClient($options = [])
197
    {
198 81
        if (is_null($this->httpClient)) {
199 81
            $client = parent::getHttpClient($options);
200 81
            if ($this->logger instanceof LoggerInterface) {
201 4
                $client->setLogger($this->logger);
202 4
            }
203 81
        }
204
205 81
        return $this->httpClient;
206
    }
207
208
209
    /**
210
     * @return string
211
     */
212 82
    protected function getBaseUrl()
213
    {
214 82
        return $this->getConfig()->getApiUrl() . '/' . $this->getConfig()->getProject() . '/';
215
    }
216
217
    /**
218
     * Executes an API request synchronously
219
     *
220
     * @param ClientRequestInterface $request
221
     * @return ApiResponseInterface
222
     * @throws InvalidTokenException
223
     * @throws ApiException
224
     * @throws \Exception
225
     */
226 78
    public function execute(ClientRequestInterface $request)
227
    {
228 78
        if ($request instanceof ContextAwareInterface) {
229 78
            $request->setContextIfNull($this->getConfig()->getContext());
230 78
        }
231 78
        $httpRequest = $this->createHttpRequest($request);
232
233
        try {
234 78
            $response = $this->getHttpClient()->execute($httpRequest);
235 78
        } catch (ApiException $exception) {
236 12
            if ($exception instanceof InvalidTokenException && !$this->tokenRefreshed) {
237 1
                $this->tokenRefreshed = true;
238 1
                $this->getOauthManager()->refreshToken();
239 1
                return $this->execute($request);
240
            }
241 12
            if ($this->getConfig()->getThrowExceptions() || !$exception->getResponse() instanceof ResponseInterface) {
242 10
                throw $exception;
243
            }
244 2
            $response = $exception->getResponse();
245
        }
246 68
        $this->logDeprecatedRequest($response, $httpRequest);
247
248 68
        $response = $request->buildResponse($response);
249
250 68
        return $response;
251
    }
252
253
    /**
254
     * Executes an API request asynchronously
255
     * @param ClientRequestInterface $request
256
     * @return ApiResponseInterface
257
     */
258 2
    public function executeAsync(ClientRequestInterface $request)
259
    {
260 2
        if ($request instanceof ContextAwareInterface) {
261 2
            $request->setContextIfNull($this->getConfig()->getContext());
262 2
        }
263 2
        $httpRequest = $this->createHttpRequest($request);
264 2
        $response = $request->buildResponse($this->getHttpClient()->executeAsync($httpRequest));
265
266 2
        $response = $response->then(
267
            function ($httpResponse) use ($httpRequest) {
268 2
                $this->logDeprecatedRequest($httpResponse, $httpRequest);
269 2
                return $httpResponse;
270
            }
271 2
        );
272
273 2
        return $response;
274
    }
275
276
    /**
277
     * @param ClientRequestInterface $request
278
     * @return RequestInterface
279
     */
280 81
    protected function createHttpRequest(ClientRequestInterface $request)
281
    {
282 81
        $token = $this->getOAuthManager()->getToken();
283
284 81
        $httpRequest = $request->httpRequest();
285
        $httpRequest = $httpRequest
286 81
            ->withHeader('Authorization', 'Bearer ' . $token->getToken())
287 81
        ;
288 81
        return $httpRequest;
289
    }
290
291
    /**
292
     * Executes API requests in batch
293
     * @return Response\ApiResponseInterface[]
294
     * @throws ApiException
295
     */
296 60
    public function executeBatch()
297
    {
298 60
        $requests = $this->getBatchHttpRequests();
299 60
        $httpResponses = $this->getHttpClient()->executeBatch($requests);
300
301 60
        $responses = [];
302 60
        foreach ($httpResponses as $key => $httpResponse) {
303 60
            $request = $this->batchRequests[$key];
304 60
            $httpRequest = $requests[$key];
305 60
            if ($httpResponse instanceof ApiException) {
306 1
                if ($this->getConfig()->getThrowExceptions() ||
307 1
                    !$httpResponse->getResponse() instanceof ResponseInterface
308 1
                ) {
309
                    throw $httpResponse;
310
                }
311 1
                $httpResponse = $httpResponse->getResponse();
312 1
            }
313 60
            $responses[$request->getIdentifier()] = $request->buildResponse($httpResponse);
314 60
            $this->logDeprecatedRequest($httpResponse, $httpRequest);
315 60
        }
316 60
        $this->batchRequests = [];
317
318 60
        return $responses;
319
    }
320
321
    /**
322
     * @param ResponseInterface $response
323
     * @param RequestInterface $request
324
     * @return $this
325
     */
326 71
    protected function logDeprecatedRequest(ResponseInterface $response, RequestInterface $request)
327
    {
328 71
        if (is_null($this->logger)) {
329 67
            return $this;
330
        }
331
332 4
        if ($response->hasHeader(static::DEPRECATION_HEADER)) {
333 2
            $message = sprintf(
334 2
                Message::DEPRECATED_METHOD,
335 2
                $request->getUri(),
336 2
                $request->getMethod(),
337 2
                $response->getHeaderLine(static::DEPRECATION_HEADER)
338 2
            );
339 2
            $this->logger->warning($message);
340 2
        }
341 4
        return $this;
342
    }
343
344
    /**
345
     * @param RequestInterface $request
346
     * @param ResponseInterface $response
347
     * @return string
348
     */
349
    protected function format(RequestInterface $request, ResponseInterface $response)
350
    {
351
        $entries = [
352
            $request->getMethod(),
353
            (string)$request->getUri(),
354
            $response->getStatusCode()
355
        ];
356
        return implode(', ', $entries);
357
    }
358
359
    /**
360
     * @return array
361
     */
362 60
    protected function getBatchHttpRequests()
363
    {
364 60
        $requests = array_map(
365 60
            function ($request) {
366 60
                return $this->createHttpRequest($request);
367 60
            },
368 60
            $this->batchRequests
369 60
        );
370
371 60
        return $requests;
372
    }
373
374
    /**
375
     * Adds a request to the batch execution queue
376
     * @param ClientRequestInterface $request
377
     * @return $this
378
     */
379 60
    public function addBatchRequest(ClientRequestInterface $request)
380
    {
381 60
        if ($request instanceof ContextAwareInterface) {
382 60
            $request->setContextIfNull($this->getConfig()->getContext());
383 60
        }
384 60
        $this->batchRequests[] = $request;
385 60
        return $this;
386
    }
387
388
    /**
389
     * Instantiates a client with the given config
390
     * @param Config $config
391
     * @return static
392
     */
393
    public static function ofConfig(Config $config)
394
    {
395
        return new static($config);
396
    }
397
398
    /**
399
     * Instantiates a client with the given config and cache adapter
400
     * @param Config $config
401
     * @param $cache
402
     * @return static
403
     */
404
    public static function ofConfigAndCache(Config $config, $cache)
405
    {
406
        return new static($config, $cache);
407
    }
408
409
    /**
410
     * Instantiates a client with the given config and a PSR-3 compliant logger
411
     * @param Config $config
412
     * @param LoggerInterface $logger
413
     * @return static
414
     */
415
    public static function ofConfigAndLogger(Config $config, LoggerInterface $logger)
416
    {
417
        return new static($config, null, $logger);
418
    }
419
420
    /**
421
     * Instantiates a client with the given config, a cache adapter and a PSR-3 compliant logger
422
     * @param Config $config
423
     * @param $cache
424
     * @param LoggerInterface $logger
425
     * @return static
426
     */
427
    public static function ofConfigCacheAndLogger(Config $config, $cache, LoggerInterface $logger)
428
    {
429
        return new static($config, $cache, $logger);
430
    }
431
}
432