Test Failed
Push — master ( 197c32...288fe1 )
by Laurens
12:52 queued 07:02
created

getAccessTokenFromApiTokenAssertion()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 6
cts 8
cp 0.75
rs 9.6666
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.0625
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
182
        try {
183 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...
184
        } catch (ClientException $exception) {
185
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
186
        }
187
    }
188
189
    /**
190
     * @throws UnprocessableEntityException
191
     */
192 2
    public function put(string $url, string $jsonData): void
193
    {
194 2
        $headers = array_merge(
195 2
            $this->getAuthorizationHeader(),
196
            [
197 2
                'content-type' => 'application/json',
198
                'Accept' => 'application/json',
199
            ]
200
        );
201
202 2
        $options =  array_merge(['headers' => $headers], ['body' => $jsonData]);
203
204
        try {
205 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...
206
        } catch (ClientException $exception) {
207
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
208
        }
209 2
    }
210
211
    /**
212
     * @throws UnprocessableEntityException
213
     */
214 1
    public function delete(string $url): void
215
    {
216
        try {
217 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...
218
        } catch (ClientException $exception) {
219
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
220
        }
221 1
    }
222
223 11
    public function getJson(ResponseInterface $response): string
224
    {
225 11
        return $response->getBody()->getContents();
226
    }
227
228 19
    private function getAuthorizationHeader(): array
229
    {
230 19
        $this->validateAccessToken();
231
232 19
        return ['Authorization' => sprintf('Bearer %s', $this->accessToken->getToken())];
233
    }
234
235 24
    private function validateAccessToken(): void
236
    {
237 24
        if ($this->accessToken->isExpired()) {
238 1
            throw new AccessTokenExpiredException(
239 1
                sprintf(
240 1
                    'Access Token was valid till \'%s\' it\'s now \'%s\'',
241 1
                    $this->accessToken->getExpires()->format('Y-m-d H:i:s.u'),
242 1
                    (new DateTime())->format('Y-m-d H:i:s.u')
243
                )
244
            );
245
        }
246 23
    }
247
248 4
    private function requestAccessToken($url, $options): AccessToken
249
    {
250 4
        $headers = ['headers' => ['Content-Type' => 'application/x-www-form-urlencoded']];
251 4
        $options = array_merge($headers, $options);
252
253 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...
254 3
        $data = json_decode($response->getBody()->getContents(), true);
255
256 3
        return new AccessToken(
257 3
            $data['access_token'],
258 3
            new DateTimeImmutable(sprintf('+%d second', $data['expires_in'])),
259 3
            $data['refresh_token'] ?? null
260
        );
261
    }
262
}
263