Completed
Push — master ( b72ee4...c33233 )
by Laurens
10:45
created

GuzzleIzettleClient   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 84.95%

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 8
dl 0
loc 208
ccs 79
cts 93
cp 0.8495
rs 10
c 0
b 0
f 0

14 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 refreshAccessToken() 0 16 1
A get() 0 12 2
A post() 0 17 2
A put() 0 18 2
A delete() 0 4 1
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\ClientInterface;
10
use GuzzleHttp\Exception\ClientException;
11
use GuzzleHttp\Exception\RequestException;
12
use LauLamanApps\IzettleApi\API\Universal\IzettlePostable;
13
use LauLamanApps\IzettleApi\Client\AccessToken;
14
use LauLamanApps\IzettleApi\Client\ApiScope;
15
use LauLamanApps\IzettleApi\Client\Exception\AccessTokenExpiredException;
16
use LauLamanApps\IzettleApi\Client\Exception\GuzzleClientExceptionHandler;
17
use LauLamanApps\IzettleApi\Exception\UnprocessableEntityException;
18
use Psr\Http\Message\ResponseInterface;
19
20
class GuzzleIzettleClient implements IzettleClientInterface
21
{
22
    /**
23
     * @var ClientInterface
24
     */
25
    private $guzzleClient;
26
27
    /**
28
     * @var string
29
     */
30
    private $clientId;
31
32
    /**
33
     * @var string
34
     */
35
    private $clientSecret;
36
37
    /**
38
     * @var AccessToken
39
     */
40
    private $accessToken;
41
42 25
    public function __construct(ClientInterface $guzzleClient, string $clientId, string $clientSecret)
43
    {
44 25
        $this->guzzleClient = $guzzleClient;
45 25
        $this->clientId = $clientId;
46 25
        $this->clientSecret = $clientSecret;
47 25
    }
48
49 23
    public function setAccessToken(AccessToken $accessToken): void
50
    {
51 23
        $this->accessToken = $accessToken;
52 23
        $this->validateAccessToken();
53 22
    }
54
55 1
    public function authoriseUserLogin(string $redirectUrl, ApiScope $apiScope): string
56
    {
57 1
        $url = self::API_AUTHORIZE_USER_LOGIN_URL;
58 1
        $url .= '?response_type=code';
59 1
        $url .= '&redirect_uri=' . $redirectUrl;
60 1
        $url .= '&client_id=' . $this->clientId;
61 1
        $url .= '&scope=' . $apiScope->getUrlParameters();
62 1
        $url .= '&state=oauth2';
63
64 1
        return $url;
65
    }
66
67
    
68
    public function getAccessTokenFromAuthorizedCode(string $redirectUrl, string $code): AccessToken
69
    {
70
        $options = [
71
           'form_params' => [
72
              'grant_type' => self::API_ACCESS_TOKEN_CODE_GRANT,
73
              'client_id' => $this->clientId,
74
              'client_secret' => $this->clientSecret,
75
              'redirect_uri' => $redirectUrl,
76
              'code' => $code
77
           ],
78
        ];
79
80
        try {
81
            $this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REQUEST_URL, $options));
82
        } catch (ClientException $exception) {
83
            GuzzleClientExceptionHandler::handleClientException($exception);
84
        }
85
86
        return $this->accessToken;
87
    }
88
    
89 2
    public function getAccessTokenFromUserLogin(string $username, string $password): AccessToken
90
    {
91
        $options = [
92
            'form_params' => [
93 2
                'grant_type' => self::API_ACCESS_TOKEN_PASSWORD_GRANT,
94 2
                'client_id' => $this->clientId,
95 2
                'client_secret' => $this->clientSecret,
96 2
                'username' => $username,
97 2
                'password' => $password
98
            ],
99
        ];
100
101
        try {
102 2
            $this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REQUEST_URL, $options));
103 1
        } catch (ClientException $exception) {
104 1
            GuzzleClientExceptionHandler::handleClientException($exception);
105
        }
106
107 1
        return $this->accessToken;
108
    }
109
110 1
    public function refreshAccessToken(?AccessToken $accessToken =  null): AccessToken
111
    {
112 1
        $accessToken = $accessToken ?? $this->accessToken;
113
        $options = [
114
            'form_params' => [
115 1
                'grant_type' => self::API_ACCESS_TOKEN_REFRESH_TOKEN_GRANT,
116 1
                'client_id' => $this->clientId,
117 1
                'client_secret' => $this->clientSecret,
118 1
                'refresh_token' => $accessToken->getRefreshToken()
119
            ],
120
        ];
121
122 1
        $this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REFRESH_TOKEN_URL, $options));
123
124 1
        return $this->accessToken;
125
    }
126
127 13
    public function get(string $url, ?array $queryParameters = null): ResponseInterface
128
    {
129 13
        $options =  array_merge(['headers' => $this->getAuthorizationHeader()], ['query' => $queryParameters]);
130
131
        try {
132 13
            $response = $this->guzzleClient->get($url, $options);
133 1
        } catch (RequestException $exception) {
134 1
            GuzzleClientExceptionHandler::handleRequestException($exception);
135
        }
136
137 12
        return $response;
138
    }
139
140
    /**
141
     * @throws UnprocessableEntityException
142
     */
143 3
    public function post(string $url, IzettlePostable $postable): ResponseInterface
144
    {
145 3
        $headers = array_merge(
146 3
            $this->getAuthorizationHeader(),
147
            [
148 3
                'content-type' => 'application/json',
149
                'Accept' => 'application/json',
150
            ]
151
        );
152
153 3
        $options =  array_merge(['headers' => $headers], ['body' => $postable->getPostBodyData()]);
154
        try {
155 3
            return $this->guzzleClient->post($url, $options);
156
        } catch (ClientException $exception) {
157
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
158
        }
159
    }
160
161
    /**
162
     * @throws UnprocessableEntityException
163
     */
164 2
    public function put(string $url, string $jsonData): void
165
    {
166 2
        $headers = array_merge(
167 2
            $this->getAuthorizationHeader(),
168
            [
169 2
                'content-type' => 'application/json',
170
                'Accept' => 'application/json',
171
            ]
172
        );
173
174 2
        $options =  array_merge(['headers' => $headers], ['body' => $jsonData]);
175
176
        try {
177 2
            $this->guzzleClient->put($url, $options);
178
        } catch (ClientException $exception) {
179
            throw new UnprocessableEntityException($exception->getResponse()->getBody()->getContents());
180
        }
181 2
    }
182
183 1
    public function delete(string $url): void
184
    {
185 1
        $this->guzzleClient->delete($url, ['headers' => $this->getAuthorizationHeader()]);
186 1
    }
187
188 11
    public function getJson(ResponseInterface $response): string
189
    {
190 11
        return $response->getBody()->getContents();
191
    }
192
193 19
    private function getAuthorizationHeader(): array
194
    {
195 19
        $this->validateAccessToken();
196
197 19
        return ['Authorization' => sprintf('Bearer %s', $this->accessToken->getToken())];
198
    }
199
200 23
    private function validateAccessToken(): void
201
    {
202 23
        if ($this->accessToken->isExpired()) {
203 1
            throw new AccessTokenExpiredException(
204 1
                sprintf(
205 1
                    'Access Token was valid till \'%s\' it\'s now \'%s\'',
206 1
                    $this->accessToken->getExpires()->format('Y-m-d H:i:s.u'),
207 1
                    (new DateTime())->format('Y-m-d H:i:s.u')
208
                )
209
            );
210
        }
211 22
    }
212
213 3
    private function requestAccessToken($url, $options): AccessToken
214
    {
215 3
        $headers = ['headers' => ['Content-Type' => 'application/x-www-form-urlencoded']];
216 3
        $options = array_merge($headers, $options);
217
218 3
        $response = $this->guzzleClient->post($url, $options);
219 2
        $data = json_decode($response->getBody()->getContents(), true);
220
221 2
        return new AccessToken(
222 2
            $data['access_token'],
223 2
            new DateTimeImmutable(sprintf('+%d second', $data['expires_in'])),
224 2
            $data['refresh_token']
225
        );
226
    }
227
}
228