CommonApiClient   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 390
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 43
eloc 119
c 1
b 0
f 0
dl 0
loc 390
rs 8.96

20 Methods

Rating   Name   Duplication   Size   Complexity  
A setHttpClient() 0 9 1
A getBaseUrl() 0 3 1
B getBaseEntrypoint() 0 19 8
A refreshTokenRequest() 0 18 1
A getCurrenciesRequest() 0 8 1
A setConfiguration() 0 5 1
A setHttpClientRequestFailureLogLevelOnce() 0 7 2
B getToken() 0 32 8
A executeRequest() 0 16 3
A getHttpClient() 0 11 3
A getListChannelSetsURL() 0 3 1
A setHttpClientRequestFailureLogLevel() 0 7 2
A getTokens() 0 3 1
A loadTokens() 0 9 2
A getCurrenciesURL() 0 3 2
A getConfiguration() 0 3 1
A listChannelSetsRequest() 0 13 1
A getAuthenticationURL() 0 3 1
A __construct() 0 8 1
A obtainTokenRequest() 0 21 2

How to fix   Complexity   

Complex Class

Complex classes like CommonApiClient often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CommonApiClient, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * PHP version 5.4 and 8
5
 *
6
 * @category  Core
7
 * @package   Payever\Core
8
 * @author    payever GmbH <[email protected]>
9
 * @author    Andrey Puhovsky <[email protected]>
10
 * @author    Hennadii.Shymanskyi <[email protected]>
11
 * @copyright 2017-2021 payever GmbH
12
 * @license   MIT <https://opensource.org/licenses/MIT>
13
 * @link      https://docs.payever.org/shopsystems/api/getting-started
14
 */
15
16
namespace Payever\ExternalIntegration\Core;
17
18
use Payever\ExternalIntegration\Core\Authorization\DummyOauthTokenList;
19
use Payever\ExternalIntegration\Core\Authorization\OauthToken;
20
use Payever\ExternalIntegration\Core\Authorization\OauthTokenList;
21
use Payever\ExternalIntegration\Core\Base\ClientConfigurationInterface;
22
use Payever\ExternalIntegration\Core\Base\CommonApiClientInterface;
23
use Payever\ExternalIntegration\Core\Base\HttpClientInterface;
24
use Payever\ExternalIntegration\Core\Base\OauthTokenInterface;
25
use Payever\ExternalIntegration\Core\Base\ResponseInterface;
26
use Payever\ExternalIntegration\Core\Http\Client\CurlClient;
27
use Payever\ExternalIntegration\Core\Http\Request;
28
use Payever\ExternalIntegration\Core\Http\RequestBuilder;
29
use Payever\ExternalIntegration\Core\Http\RequestEntity\AuthenticationRequest;
30
use Payever\ExternalIntegration\Core\Http\ResponseEntity\AuthenticationResponse;
31
use Payever\ExternalIntegration\Core\Http\ResponseEntity\GetCurrenciesResponse;
32
use Payever\ExternalIntegration\Core\Http\ResponseEntity\ListChannelSetsResponse;
33
use Psr\Log\LoggerAwareInterface;
34
use Psr\Log\LogLevel;
35
36
/**
37
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
38
 * @SuppressWarnings(PHPMD.MissingImport)
39
 * @SuppressWarnings(PHPMD.StaticAccess)
40
 */
41
class CommonApiClient implements CommonApiClientInterface
42
{
43
    const URL_SANDBOX = 'https://proxy.staging.devpayever.com/';
44
    const URL_LIVE    = 'https://proxy.payever.org/';
45
46
    const SUB_URL_AUTH              = 'oauth/v2/token';
47
    const SUB_URL_LIST_CHANNEL_SETS = 'api/shop/oauth/%s/channel-sets';
48
    const SUB_URL_CURRENCY          = 'api/rest/v1/currency';
49
50
    const FORBIDDEN_ERROR_CODE = 403;
51
52
    /**
53
     * Stores oAuth Authentication Tokens
54
     *
55
     * @var OauthTokenList $tokens
56
     */
57
    protected $tokens;
58
59
    /**
60
     * Stores API Configuration
61
     *
62
     * @var ClientConfigurationInterface $configuration
63
     */
64
    protected $configuration;
65
66
    /**
67
     * Stores current Client
68
     *
69
     * @var HttpClientInterface $httpClient
70
     */
71
    protected $httpClient;
72
73
    /**
74
     * @param ClientConfigurationInterface $clientConfiguration
75
     * @param OauthTokenList $oauthTokenList
76
     * @param HttpClientInterface $httpClient
77
     *
78
     * @throws \Exception
79
     */
80
    public function __construct(
81
        ClientConfigurationInterface $clientConfiguration,
82
        OauthTokenList $oauthTokenList = null,
83
        HttpClientInterface $httpClient = null
84
    ) {
85
        $this->configuration = $clientConfiguration;
86
        $this->httpClient = $httpClient;
87
        $this->loadTokens($oauthTokenList);
88
    }
89
90
    /**
91
     * Returns Base URL to payever Payments API
92
     *
93
     * @return string
94
     */
95
    public function getBaseUrl()
96
    {
97
        return $this->getBaseEntrypoint(true);
98
    }
99
100
    /**
101
     * Returns current configuration
102
     *
103
     * @return ClientConfigurationInterface
104
     */
105
    public function getConfiguration()
106
    {
107
        return $this->configuration;
108
    }
109
110
    /**
111
     * Returns current OauthToken list
112
     *
113
     * @return OauthTokenList
114
     */
115
    public function getTokens()
116
    {
117
        return $this->tokens;
118
    }
119
120
    /**
121
     * Overrides configuration
122
     *
123
     * @param ClientConfigurationInterface $configuration
124
     */
125
    public function setConfiguration(ClientConfigurationInterface $configuration)
126
    {
127
        $this->configuration = $configuration;
128
129
        $this->getTokens()->clear()->save();
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function getHttpClient()
136
    {
137
        if ($this->httpClient === null) {
138
            $this->httpClient = new CurlClient();
139
        }
140
141
        if ($this->httpClient instanceof LoggerAwareInterface) {
142
            $this->httpClient->setLogger($this->configuration->getLogger());
143
        }
144
145
        return $this->httpClient;
146
    }
147
148
    /**
149
     * @param string $logLevel
150
     * @return $this
151
     */
152
    public function setHttpClientRequestFailureLogLevel($logLevel = LogLevel::CRITICAL)
153
    {
154
        if ($this->getHttpClient() instanceof CurlClient) {
155
            $this->getHttpClient()->setLogLevel($logLevel);
156
        }
157
158
        return $this;
159
    }
160
161
    /**
162
     * @param string $logLevel
163
     * @return $this
164
     */
165
    public function setHttpClientRequestFailureLogLevelOnce($logLevel)
166
    {
167
        if ($this->getHttpClient() instanceof CurlClient) {
168
            $this->getHttpClient()->setLogLevelOnce($logLevel);
169
        }
170
171
        return $this;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function setHttpClient(HttpClientInterface $httpClient)
178
    {
179
        $this->configuration->getLogger()->debug(
180
            sprintf('Got new HTTP Client: %s', get_class($httpClient))
181
        );
182
183
        $this->httpClient = $httpClient;
184
185
        return $this;
186
    }
187
188
    /**
189
     * Returns Authentication OauthToken
190
     *
191
     * @param string $scope OauthToken scope
192
     *
193
     * @return OauthTokenInterface
194
     *
195
     * @throws \Exception
196
     */
197
    public function getToken($scope = OauthTokenInterface::SCOPE_PAYMENT_ACTIONS)
198
    {
199
        $key = md5($this->getConfiguration()->getHash() . $scope);
200
201
        $this->configuration->getLogger()->debug(sprintf('Getting OAuth token. Hash: %s', $key));
202
203
        /** @var OauthTokenInterface $token */
204
        $token = $this->getTokens()->get($key);
205
206
        if (!$token || ($token instanceof OauthTokenInterface && $token->isExpired() && !$token->isRefreshable())) {
0 ignored issues
show
introduced by
$token is of type Payever\ExternalIntegrat...ase\OauthTokenInterface, thus it always evaluated to true.
Loading history...
207
            $tokenData = $this->obtainTokenRequest($scope)->getResponseEntity()->toArray();
208
            /** @var OauthTokenInterface $token */
209
            $token = $this->getTokens()->add(
210
                $key,
211
                $this->getTokens()->create()->load($tokenData)->setUpdatedAt()
212
            )->get($key);
213
214
            $this->getTokens()->save();
215
        } elseif ($token instanceof OauthTokenInterface && $token->isExpired() && $token->isRefreshable()) {
216
            $tokenData = $this->refreshTokenRequest($token)->getResponseEntity()->toArray();
217
218
            $token->load($tokenData)->setUpdatedAt();
219
220
            $this->getTokens()->save();
221
        }
222
223
        $this->configuration->getLogger()->debug(
224
            sprintf('Got OAuth token. Hash: %s', $key),
225
            $token->getParams()
226
        );
227
228
        return $token;
229
    }
230
231
    /**
232
     * {@inheritdoc}
233
     *
234
     * @throws \Exception
235
     */
236
    public function getCurrenciesRequest($lang = '')
237
    {
238
        $request = RequestBuilder::get($this->getCurrenciesURL($lang))
239
            ->setResponseEntity(new GetCurrenciesResponse())
240
            ->build()
241
        ;
242
243
        return $this->getHttpClient()->execute($request);
244
    }
245
246
    /**
247
     * {@inheritdoc}
248
     *
249
     * @throws \Exception
250
     */
251
    public function listChannelSetsRequest($businessUuid)
252
    {
253
        $this->configuration->assertLoaded();
254
255
        $request = RequestBuilder::get($this->getListChannelSetsURL($businessUuid))
256
            ->addRawHeader(
257
                $this->getToken(OauthToken::SCOPE_PAYMENT_INFO)->getAuthorizationString()
258
            )
259
            ->setResponseEntity(new ListChannelSetsResponse())
260
            ->build()
261
        ;
262
263
        return $this->executeRequest($request, OauthToken::SCOPE_PAYMENT_INFO);
264
    }
265
266
    /**
267
     * @param bool $staticBind
268
     * @return string
269
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
270
     */
271
    protected function getBaseEntrypoint($staticBind = false)
272
    {
273
        switch ($this->configuration->getApiMode()) {
274
            case ClientConfiguration::API_MODE_SANDBOX:
275
                $default = $staticBind ? static::URL_SANDBOX : self::URL_SANDBOX;
276
                $url = $this->configuration->getCustomSandboxUrl() ?: $default;
277
                break;
278
            case ClientConfiguration::API_MODE_LIVE:
279
            default:
280
                $default = $staticBind ? static::URL_LIVE : self::URL_LIVE;
281
                $url = $this->configuration->getCustomLiveUrl() ?: $default;
282
                break;
283
        }
284
285
        if (substr($url, -1) != '/') {
286
            $url .= '/';
287
        }
288
289
        return $url;
290
    }
291
292
    /**
293
     * Loads Tokens
294
     *
295
     * @param OauthTokenList|null $oauthTokenList
296
     *
297
     * @return $this
298
     */
299
    protected function loadTokens(OauthTokenList $oauthTokenList = null)
300
    {
301
        if (is_null($oauthTokenList)) {
302
            $oauthTokenList = new DummyOauthTokenList();
303
        }
304
305
        $this->tokens = $oauthTokenList->load();
306
307
        return $this;
308
    }
309
310
    /**
311
     * Requests new oAuth OauthToken which will be used further
312
     *
313
     * @link https://docs.payever.org/shopsystems/api/getting-started/authentication Documentation
314
     *
315
     * @param string $scope Scope in which the token will be used
316
     *
317
     * @return ResponseInterface
318
     *
319
     * @throws \Exception
320
     */
321
    protected function obtainTokenRequest($scope)
322
    {
323
        $this->configuration->assertLoaded();
324
325
        if (!in_array($scope, OauthToken::getScopes())) {
326
            throw new \Exception('Scope provided is not valid');
327
        }
328
329
        $requestEntity = new AuthenticationRequest();
330
        $requestEntity
331
            ->setClientId($this->configuration->getClientId())
332
            ->setClientSecret($this->configuration->getClientSecret())
333
            ->setScope($scope)
334
            ->setGrantType(OauthTokenInterface::GRAND_TYPE_OBTAIN_TOKEN);
335
336
        $request = RequestBuilder::post($this->getAuthenticationURL())
337
            ->setRequestEntity($requestEntity)
338
            ->setResponseEntity(new AuthenticationResponse())
339
            ->build();
340
341
        return $this->getHttpClient()->execute($request);
342
    }
343
344
    /**
345
     * Requests for an updated oAuth OauthToken data
346
     *
347
     * @param OauthTokenInterface|object|array $token OauthToken for the update
348
     *
349
     * @return ResponseInterface
350
     *
351
     * @throws \Exception
352
     */
353
    protected function refreshTokenRequest(OauthTokenInterface $token)
354
    {
355
        $this->configuration->assertLoaded();
356
357
        $requestEntity = new AuthenticationRequest();
358
        $requestEntity
359
            ->setClientId($this->configuration->getClientId())
360
            ->setClientSecret($this->configuration->getClientSecret())
361
            ->setScope($token->getScope())
362
            ->setGrantType(OauthTokenInterface::GRAND_TYPE_REFRESH_TOKEN)
363
            ->setRefreshToken($token->getRefreshToken());
364
365
        $request = RequestBuilder::post($this->getAuthenticationURL())
366
            ->setRequestEntity($requestEntity)
367
            ->setResponseEntity(new AuthenticationResponse())
368
            ->build();
369
370
        return $this->getHttpClient()->execute($request);
371
    }
372
373
    /**
374
     * @param Request $request
375
     * @param string $scope
376
     *
377
     * @return \Payever\ExternalIntegration\Core\Http\Response
378
     * @throws \Exception
379
     */
380
    protected function executeRequest($request, $scope = OauthToken::SCOPE_PAYMENT_ACTIONS)
381
    {
382
        try {
383
            return $this->getHttpClient()->execute($request);
384
        } catch (\Exception $exception) {
385
            if ($exception->getCode() === self::FORBIDDEN_ERROR_CODE) {
386
                $this->getTokens()->clear()->save();
387
388
                $newToken = $this->getToken($scope)->getAuthorizationString();
389
                $pieces = explode(':', $newToken, 2);
390
                $request->addHeader($pieces[0], $pieces[1]);
391
392
                return $this->getHttpClient()->execute($request);
393
            }
394
395
            throw $exception;
396
        }
397
    }
398
399
    /**
400
     * Returns URL for Authentication path
401
     *
402
     * @return string
403
     */
404
    protected function getAuthenticationURL()
405
    {
406
        return $this->getBaseEntrypoint() . self::SUB_URL_AUTH;
407
    }
408
409
    /**
410
     * Returns URL for Available Channel Sets path
411
     *
412
     * @param string $businessUuid
413
     *
414
     * @return string
415
     */
416
    protected function getListChannelSetsURL($businessUuid)
417
    {
418
        return $this->getBaseEntrypoint() . sprintf(self::SUB_URL_LIST_CHANNEL_SETS, $businessUuid);
419
    }
420
421
    /**
422
     * Returns URL to Currencies path
423
     *
424
     * @param string $lang
425
     *
426
     * @return string
427
     */
428
    protected function getCurrenciesURL($lang = '')
429
    {
430
        return $this->getBaseEntrypoint() . self::SUB_URL_CURRENCY . (empty($lang) ? '' : '?_locale=' . $lang);
431
    }
432
}
433