HttpClient   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 68
dl 0
loc 228
ccs 62
cts 62
cp 1
rs 10
c 0
b 0
f 0
wmc 22

10 Methods

Rating   Name   Duplication   Size   Complexity  
A generateAccessToken() 0 10 2
A __construct() 0 13 2
A setResponseFilter() 0 5 1
A __call() 0 7 2
A setHttp() 0 5 1
A getConfig() 0 7 2
A setCacheManager() 0 5 1
B request() 0 23 7
A receiveAccessToken() 0 22 2
A hasAccessToken() 0 3 2
1
<?php
2
3
namespace IproSoftwareApi;
4
5
use BadMethodCallException;
6
use GuzzleHttp\ClientInterface;
7
use GuzzleHttp\Exception\GuzzleException;
8
use IproSoftwareApi\Contracts\AccessToken as AccessTokenInterface;
9
use IproSoftwareApi\Contracts\AccessTokenCacher;
10
use IproSoftwareApi\DTOs\ClientCredentials;
11
use IproSoftwareApi\Exceptions\IproSoftwareApiAccessTokenException;
12
use IproSoftwareApi\Exceptions\IproSoftwareApiException;
13
use Psr\Http\Message\ResponseInterface;
14
15
/**
16
 * Class HttpClient.
17
 *
18
 * @method get($path = '', array $options = [])
19
 * @method post($path = '', array $options = [])
20
 * @method put($path = '', array $options = [])
21
 * @method delete($path = '', array $options = [])
22
 * @method head($path = '', array $options = [])
23
 * @method patch($path = '', array $options = [])
24
 */
25
class HttpClient implements Contracts\HttpClient
26
{
27
    /**
28
     * HTTP Methods.
29
     */
30
    const HTTP_METHOD_GET    = 'GET';
31
    const HTTP_METHOD_POST   = 'POST';
32
    const HTTP_METHOD_PUT    = 'PUT';
33
    const HTTP_METHOD_DELETE = 'DELETE';
34
    const HTTP_METHOD_HEAD   = 'HEAD';
35
    const HTTP_METHOD_PATCH  = 'PATCH';
36
37
    const HTTP_METHODS = [
38
        self::HTTP_METHOD_GET,
39
        self::HTTP_METHOD_POST,
40
        self::HTTP_METHOD_PUT,
41
        self::HTTP_METHOD_DELETE,
42
        self::HTTP_METHOD_HEAD,
43
        self::HTTP_METHOD_PATCH,
44
    ];
45
46
    /**
47
     * @var string
48
     */
49
    protected $accessTokenClass;
50
51
    /**
52
     * @var AccessTokenCacher
53
     */
54
    protected $cacheManager;
55
56
    /**
57
     * @var null|callable
58
     */
59
    protected $responseFilter;
60
61
    /**
62
     * @var ClientCredentials
63
     */
64
    protected $clientCredentials;
65
66
    /**
67
     * @var ClientInterface
68
     */
69
    protected $http;
70
71
    /**
72
     * @var AccessTokenInterface
73
     */
74
    protected $accessToken;
75
76
    /**
77
     * HttpClient constructor.
78
     *
79
     * @param AccessTokenCacher $cacheManager
80
     * @param ClientCredentials $clientCredentials
81
     * @param array $httpConfiguration
82
     */
83 11
    public function __construct(ClientCredentials $clientCredentials, AccessTokenCacher $cacheManager, array $httpConfiguration = [])
84
    {
85 11
        $this->cacheManager      = $cacheManager;
86 11
        $this->clientCredentials = $clientCredentials;
87
88 11
        $this->accessTokenClass = $httpConfiguration['access_token_class']
89 11
            ?? \IproSoftwareApi\AccessToken\AccessToken::class;
90
91 11
        $configs = $httpConfiguration['client_conf'] ?? [];
92 11
        if (!isset($configs['base_uri'])) {
93 11
            $configs['base_uri'] = $this->clientCredentials->apiHost;
94
        }
95 11
        $this->http = new \GuzzleHttp\Client($configs);
96
    }
97
98
    /**
99
     * @param $method
100
     * @param $arguments
101
     *
102
     * @return mixed|ResponseInterface
103
     * @throws GuzzleException
104
     *
105
     * @throws IproSoftwareApiAccessTokenException
106
     */
107 1
    public function __call($method, $arguments)
108
    {
109 1
        if (in_array(strtoupper($method), self::HTTP_METHODS)) {
110 1
            return $this->request(strtoupper($method), $arguments[0], $arguments[1] ?? []);
111
        }
112
113 1
        throw new BadMethodCallException('Method ' . $method . ' not found on ' . get_class() . '.', 500);
114
    }
115
116
    /**
117
     * @param AccessTokenCacher $cacheManager
118
     *
119
     * @return static
120
     */
121 3
    public function setCacheManager(AccessTokenCacher $cacheManager): static
122
    {
123 3
        $this->cacheManager = $cacheManager;
124
125 3
        return $this;
126
    }
127
128
    /**
129
     * @param callable|null $responseFilter
130
     *
131
     * @return static
132
     */
133 1
    public function setResponseFilter(?callable $responseFilter): static
134
    {
135 1
        $this->responseFilter = $responseFilter;
136
137 1
        return $this;
138
    }
139
140
    /**
141
     * @param ClientInterface $http
142
     *
143
     * @return static
144
     */
145 5
    public function setHttp(ClientInterface $http): static
146
    {
147 5
        $this->http = $http;
148
149 5
        return $this;
150
    }
151
152
    /**
153
     * @param null $option
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $option is correct as it would always require null to be passed?
Loading history...
154
     *
155
     * @return mixed
156
     * @throws IproSoftwareApiException
157
     *
158
     */
159 2
    public function getConfig($option = null)
160
    {
161 2
        if (!is_null($this->http)) {
162 2
            return $this->http->getConfig($option);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\ClientInterface::getConfig() has been deprecated: ClientInterface::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

162
            return /** @scrutinizer ignore-deprecated */ $this->http->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...
163
        }
164
165 1
        throw new IproSoftwareApiException('Http client not specified');
166
    }
167
168
    /**
169
     * @param $method
170
     * @param string $path
171
     * @param array $options
172
     *
173
     * @return mixed|ResponseInterface
174
     * @throws IproSoftwareApiAccessTokenException
175
     *
176
     * @throws GuzzleException
177
     */
178 3
    public function request($method, $path = '', array $options = []): ResponseInterface
179
    {
180 3
        if (!$this->hasAccessToken()) {
181 1
            $this->generateAccessToken();
182
        }
183
184 3
        if (!isset($options['headers']['Authorization'])) {
185 3
            $options['headers']['Authorization'] = $this->accessToken->getAuthorizationHeader();
0 ignored issues
show
Bug introduced by
The method getAuthorizationHeader() does not exist on null. ( Ignorable by Annotation )

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

185
            /** @scrutinizer ignore-call */ 
186
            $options['headers']['Authorization'] = $this->accessToken->getAuthorizationHeader();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
186
        }
187
188 3
        if (is_string($path) && !empty($path)) {
189 3
            if ($path[0] == '/') {
190 1
                $path = substr($path, 1);
191
            }
192
        }
193
194 3
        $response = $this->http->request($method, $path, $options);
195
196 3
        if (is_callable($this->responseFilter)) {
197 1
            $response = ($this->responseFilter)($response, $options, $path, $method);
198
        }
199
200 3
        return $response;
201
    }
202
203
    /**
204
     * @return bool
205
     */
206 7
    public function hasAccessToken(): bool
207
    {
208 7
        return $this->accessToken instanceof AccessTokenInterface && $this->accessToken->hasAccessToken();
209
    }
210
211
    /**
212
     * @return AccessTokenInterface
213
     * @throws IproSoftwareApiAccessTokenException
214
     *
215
     */
216 4
    public function generateAccessToken(): AccessTokenInterface
217
    {
218 4
        $this->accessToken = $this->cacheManager->get();
219
220
        // If empty access token or expired then make request for new token
221 4
        if (!$this->hasAccessToken()) {
222 2
            $this->receiveAccessToken();
223
        }
224
225 3
        return $this->accessToken;
226
    }
227
228
    /**
229
     * @throws IproSoftwareApiAccessTokenException
230
     */
231 2
    protected function receiveAccessToken()
232
    {
233 2
        $response = $this->http->post($this->clientCredentials->tokenEndpoint, [
234 2
            'auth' => [
235 2
                $this->clientCredentials->clientId,
236 2
                $this->clientCredentials->clientSecret,
237 2
            ],
238 2
            'form_params' => [
239 2
                'grant_type' => 'client_credentials',
240 2
            ],
241 2
        ]);
242
243 2
        if ($response->getStatusCode() != 200) {
244 1
            throw new IproSoftwareApiAccessTokenException($response, 'Get Access Token Error');
245
        }
246
247 1
        $this->accessToken = call_user_func(
248 1
            [$this->accessTokenClass, 'makeFromApiResponse'],
249 1
            $response
250 1
        );
251
252 1
        $this->cacheManager->put($this->accessToken);
253
    }
254
}
255