Passed
Pull Request — master (#47)
by
unknown
02:10
created

GuzzleIzettleClient   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 83.02%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 9
dl 0
loc 240
c 0
b 0
f 0
ccs 88
cts 106
cp 0.8302
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setAccessToken() 0 5 1
A authoriseUserLogin() 0 11 1
A getAccessTokenFromAuthorizedCode() 0 20 2
A getAccessTokenFromUserLogin() 0 20 2
A getAccessTokenFromApiTokenAssertion() 0 18 2
A refreshAccessToken() 0 22 2
A get() 0 12 2
A post() 0 17 2
A put() 0 18 2
A delete() 0 8 2
A getJson() 0 4 1
A getAuthorizationHeader() 0 6 1
A validateAccessToken() 0 12 2
A requestAccessToken() 0 14 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LauLamanApps\IzettleApi;
6
7
use DateTime;
8
use DateTimeImmutable;
9
use GuzzleHttp\Client;
10
use GuzzleHttp\ClientInterface;
11
use GuzzleHttp\Exception\ClientException;
12
use GuzzleHttp\Exception\RequestException;
13
use LauLamanApps\IzettleApi\API\Universal\IzettlePostable;
14
use LauLamanApps\IzettleApi\Client\AccessToken;
15
use LauLamanApps\IzettleApi\Client\ApiScope;
16
use LauLamanApps\IzettleApi\Client\Exception\AccessTokenExpiredException;
17
use LauLamanApps\IzettleApi\Client\Exception\AccessTokenNotRefreshableException;
18
use LauLamanApps\IzettleApi\Client\Exception\GuzzleClientExceptionHandler;
19
use LauLamanApps\IzettleApi\Exception\UnprocessableEntityException;
20
use Psr\Http\Message\ResponseInterface;
21
22
class GuzzleIzettleClient implements IzettleClientInterface
23
{
24
    /**
25
     * @var ClientInterface|Client
26
     */
27
    private $guzzleClient;
28
29
    /**
30
     * @var string
31
     */
32
    private $clientId;
33
34
    /**
35
     * @var string
36
     */
37
    private $clientSecret;
38
39
    /**
40
     * @var AccessToken
41
     */
42
    private $accessToken;
43
44 26
    public function __construct(ClientInterface $guzzleClient, string $clientId, string $clientSecret)
45
    {
46 26
        $this->guzzleClient = $guzzleClient;
47 26
        $this->clientId = $clientId;
48 26
        $this->clientSecret = $clientSecret;
49 26
    }
50
51 24
    public function setAccessToken(AccessToken $accessToken): void
52
    {
53 24
        $this->accessToken = $accessToken;
54 24
        $this->validateAccessToken();
55 23
    }
56
57 1
    public function authoriseUserLogin(string $redirectUrl, ApiScope $apiScope): string
58
    {
59 1
        $url = self::API_AUTHORIZE_USER_LOGIN_URL;
60 1
        $url .= '?response_type=code';
61 1
        $url .= '&redirect_uri=' . $redirectUrl;
62 1
        $url .= '&client_id=' . $this->clientId;
63 1
        $url .= '&scope=' . $apiScope->getUrlParameters();
64 1
        $url .= '&state=oauth2';
65
66 1
        return $url;
67
    }
68
69
    
70
    public function getAccessTokenFromAuthorizedCode(string $redirectUrl, string $code): AccessToken
71
    {
72
        $options = [
73
           'form_params' => [
74
              'grant_type' => self::API_ACCESS_TOKEN_CODE_GRANT,
75
              'client_id' => $this->clientId,
76
              'client_secret' => $this->clientSecret,
77
              'redirect_uri' => $redirectUrl,
78
              'code' => $code
79
           ],
80
        ];
81
82
        try {
83
            $this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REQUEST_URL, $options));
84
        } catch (ClientException $exception) {
85
            GuzzleClientExceptionHandler::handleClientException($exception);
86
        }
87
88
        return $this->accessToken;
89
    }
90
    
91 2
    public function getAccessTokenFromUserLogin(string $username, string $password): AccessToken
92
    {
93
        $options = [
94
            'form_params' => [
95 2
                'grant_type' => self::API_ACCESS_TOKEN_PASSWORD_GRANT,
96 2
                'client_id' => $this->clientId,
97 2
                'client_secret' => $this->clientSecret,
98 2
                'username' => $username,
99 2
                'password' => $password
100
            ],
101
        ];
102
103
        try {
104 2
            $this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REQUEST_URL, $options));
105 1
        } catch (ClientException $exception) {
106 1
            GuzzleClientExceptionHandler::handleClientException($exception);
107
        }
108
109 1
        return $this->accessToken;
110
    }
111
112 1
    public function getAccessTokenFromApiTokenAssertion(string $assertion): AccessToken
113
    {
114
        $options = [
115
            'form_params' => [
116 1
                'grant_type' => self::API_ACCESS_ASSERTION_GRANT,
117 1
                'client_id' => $this->clientId,
118 1
                'assertion' => $assertion
119
            ],
120
        ];
121
122
        try {
123 1
            $this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REQUEST_URL, $options));
124
        } catch (ClientException $exception) {
125
            GuzzleClientExceptionHandler::handleClientException($exception);
126
        }
127
128 1
        return $this->accessToken;
129
    }
130
131 1
    public function refreshAccessToken(?AccessToken $accessToken =  null): AccessToken
132
    {
133 1
        $accessToken = $accessToken ?? $this->accessToken;
134
135 1
        $refreshToken = $accessToken->getRefreshToken();
136 1
        if ($refreshToken === null) {
137 1
            throw new AccessTokenNotRefreshableException('This access token cannot be renewed.');
138
        }
139
140
        $options = [
141
            'form_params' => [
142 1
                'grant_type' => self::API_ACCESS_TOKEN_REFRESH_TOKEN_GRANT,
143 1
                'client_id' => $this->clientId,
144 1
                'client_secret' => $this->clientSecret,
145 1
                'refresh_token' => $refreshToken
146
            ],
147
        ];
148
149 1
        $this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REFRESH_TOKEN_URL, $options));
150
151 1
        return $this->accessToken;
152
    }
153
154 13
    public function get(string $url, ?array $queryParameters = null): ResponseInterface
155
    {
156 13
        $options =  array_merge(['headers' => $this->getAuthorizationHeader()], ['query' => $queryParameters]);
157
158
        try {
159 13
            $response = $this->guzzleClient->get($url, $options);
0 ignored issues
show
Bug introduced by
The method get does only exist in GuzzleHttp\Client, but not in GuzzleHttp\ClientInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
160 1
        } catch (RequestException $exception) {
161 1
            GuzzleClientExceptionHandler::handleRequestException($exception);
162
        }
163
164 12
        return $response;
165
    }
166
167
    /**
168
     * @throws UnprocessableEntityException
169
     */
170 3
    public function post(string $url, IzettlePostable $postable): ResponseInterface
171
    {
172 3
        $headers = array_merge(
173 3
            $this->getAuthorizationHeader(),
174
            [
175 3
                'content-type' => 'application/json',
176
                'Accept' => 'application/json',
177
            ]
178
        );
179
180 3
        $options =  array_merge(['headers' => $headers], ['body' => $postable->getPostBodyData()]);
181
        try {
182 3
            return $this->guzzleClient->post($url, $options);
0 ignored issues
show
Bug introduced by
The method post does only exist in GuzzleHttp\Client, but not in GuzzleHttp\ClientInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
183
        } catch (ClientException $exception) {
184
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
185
        }
186
    }
187
188
    /**
189
     * @throws UnprocessableEntityException
190
     */
191 2
    public function put(string $url, string $jsonData): void
192
    {
193 2
        $headers = array_merge(
194 2
            $this->getAuthorizationHeader(),
195
            [
196 2
                'content-type' => 'application/json',
197
                'Accept' => 'application/json',
198
            ]
199
        );
200
201 2
        $options =  array_merge(['headers' => $headers], ['body' => $jsonData]);
202
203
        try {
204 2
            $this->guzzleClient->put($url, $options);
0 ignored issues
show
Bug introduced by
The method put does only exist in GuzzleHttp\Client, but not in GuzzleHttp\ClientInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
205
        } catch (ClientException $exception) {
206
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
207
        }
208 2
    }
209
210
    /**
211
     * @throws UnprocessableEntityException
212
     */
213 1
    public function delete(string $url): void
214
    {
215
        try {
216 1
            $this->guzzleClient->delete($url, ['headers' => $this->getAuthorizationHeader()]);
0 ignored issues
show
Bug introduced by
The method delete does only exist in GuzzleHttp\Client, but not in GuzzleHttp\ClientInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
217
        } catch (ClientException $exception) {
218
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
219
        }
220 1
    }
221
222 11
    public function getJson(ResponseInterface $response): string
223
    {
224 11
        return $response->getBody()->getContents();
225
    }
226
227 19
    private function getAuthorizationHeader(): array
228
    {
229 19
        $this->validateAccessToken();
230
231 19
        return ['Authorization' => sprintf('Bearer %s', $this->accessToken->getToken())];
232
    }
233
234 24
    private function validateAccessToken(): void
235
    {
236 24
        if ($this->accessToken->isExpired()) {
237 1
            throw new AccessTokenExpiredException(
238 1
                sprintf(
239 1
                    'Access Token was valid till \'%s\' it\'s now \'%s\'',
240 1
                    $this->accessToken->getExpires()->format('Y-m-d H:i:s.u'),
241 1
                    (new DateTime())->format('Y-m-d H:i:s.u')
242
                )
243
            );
244
        }
245 23
    }
246
247 4
    private function requestAccessToken($url, $options): AccessToken
248
    {
249 4
        $headers = ['headers' => ['Content-Type' => 'application/x-www-form-urlencoded']];
250 4
        $options = array_merge($headers, $options);
251
252 4
        $response = $this->guzzleClient->post($url, $options);
0 ignored issues
show
Bug introduced by
The method post does only exist in GuzzleHttp\Client, but not in GuzzleHttp\ClientInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
253 3
        $data = json_decode($response->getBody()->getContents(), true);
254
255 3
        return new AccessToken(
256 3
            $data['access_token'],
257 3
            new DateTimeImmutable(sprintf('+%d second', $data['expires_in'])),
258 3
            $data['refresh_token'] ?? null
259
        );
260
    }
261
}
262