Skip to content

Commit b19cdd7

Browse files
committed
refactored Client methods for better extensibility
1 parent 0c7629a commit b19cdd7

File tree

3 files changed

+162
-167
lines changed

3 files changed

+162
-167
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [0.3.x (Unreleased)](https://github.com/onlime/bexio-api-client/compare/0.3.1...main)
44

55
- Fix release comparison links in CHANGELOG.
6+
- Refactored most `Client` methods into `AbstractClient` for better extensibility.
67

78
## [0.3.1 (2022-03-23)](https://github.com/onlime/bexio-api-client/compare/0.3.0...0.3.1)
89

src/Bexio/AbstractClient.php

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,167 @@
11
<?php
22
namespace Bexio;
33

4+
use Jumbojett\OpenIDConnectClient;
5+
46
abstract class AbstractClient
57
{
8+
const PROVIDER_URL = 'https://idp.bexio.com';
9+
const API_URL = 'https://api.bexio.com';
10+
const API_DEFAULT_VERSION = '2.0';
11+
612
const METHOD_GET = 'GET';
713
const METHOD_POST = 'POST';
814
const METHOD_PUT = 'PUT';
915
const METHOD_DELETE = 'DELETE';
1016
const METHOD_PATCH = 'PATCH';
1117

12-
abstract protected function request(string $path = '', string $method = self::METHOD_GET, array $data = [], array $queryParams = []);
18+
private array $config;
19+
private string $accessToken;
20+
private string $refreshToken;
21+
22+
/**
23+
* Client constructor.
24+
*
25+
* @param string $clientId
26+
* @param string $clientSecret
27+
* @param string|null $redirectUrl
28+
*/
29+
public function __construct(string $clientId, string $clientSecret, string $redirectUrl = null)
30+
{
31+
$this->config = [
32+
'clientId' => $clientId,
33+
'clientSecret' => $clientSecret,
34+
'redirectUrl' => $redirectUrl,
35+
];
36+
}
37+
38+
public function setClientId($clientId)
39+
{
40+
$this->config['clientId'] = $clientId;
41+
}
42+
43+
public function getClientId()
44+
{
45+
return $this->config['clientId'];
46+
}
47+
48+
public function setClientSecret($clientId)
49+
{
50+
$this->config['clientSecret'] = $clientId;
51+
}
52+
53+
public function getClientSecret()
54+
{
55+
return $this->config['clientSecret'];
56+
}
57+
58+
public function setRedirectUrl($redirectUrl)
59+
{
60+
$this->config['redirectUrl'] = $redirectUrl;
61+
}
62+
63+
public function getRedirectUrl()
64+
{
65+
return $this->config['redirectUrl'];
66+
}
67+
68+
public function setAccessToken(string $accessToken)
69+
{
70+
$this->accessToken = $accessToken;
71+
}
72+
73+
public function getAccessToken()
74+
{
75+
return $this->accessToken;
76+
}
77+
78+
public function setRefreshToken(string $refreshToken)
79+
{
80+
$this->refreshToken = $refreshToken;
81+
}
82+
83+
public function getRefreshToken()
84+
{
85+
return $this->refreshToken;
86+
}
87+
88+
public function persistTokens(string $tokensFile): bool
89+
{
90+
return false !== file_put_contents($tokensFile, json_encode([
91+
'accessToken' => $this->getAccessToken(),
92+
'refreshToken' => $this->getRefreshToken()
93+
]));
94+
}
95+
96+
public function loadTokens(string $tokensFile)
97+
{
98+
if (!file_exists($tokensFile)) {
99+
throw new \Exception('Tokens file not found: ' . $tokensFile);
100+
}
101+
$tokens = json_decode(file_get_contents($tokensFile));
102+
103+
$this->setAccessToken($tokens->accessToken);
104+
$this->setRefreshToken($tokens->refreshToken);
105+
106+
// Refresh access token if it is expired
107+
if ($this->isAccessTokenExpired()) {
108+
$this->refreshToken();
109+
$this->persistTokens($tokensFile);
110+
}
111+
}
112+
113+
public function getOpenIDConnectClient(): OpenIDConnectClient
114+
{
115+
$oidc = new OpenIDConnectClient(
116+
self::PROVIDER_URL,
117+
$this->getClientId(),
118+
$this->getClientSecret()
119+
);
120+
$oidc->setAccessToken($this->accessToken);
121+
return $oidc;
122+
}
123+
124+
public function authenticate($scopes)
125+
{
126+
if (!is_array($scopes)) {
127+
$scopes = explode(' ', $scopes);
128+
}
129+
$oidc = $this->getOpenIDConnectClient();
130+
$oidc->setRedirectURL($this->getRedirectUrl());
131+
$oidc->addScope($scopes);
132+
$oidc->authenticate();
133+
134+
$this->setAccessToken($oidc->getAccessToken());
135+
$this->setRefreshToken($oidc->getRefreshToken());
136+
}
137+
138+
public function isAccessTokenExpired($gracePeriod = 30): bool
139+
{
140+
if (!$this->accessToken) {
141+
return true;
142+
}
143+
$payload = $this->getOpenIDConnectClient()->getAccessTokenPayload();
144+
$expiry = $payload->exp ?? 0;
145+
return time() > ($expiry - $gracePeriod);
146+
}
147+
148+
public function refreshToken()
149+
{
150+
$oidc = $this->getOpenIDConnectClient();
151+
$oidc->refreshToken($this->getRefreshToken());
152+
$this->setAccessToken($oidc->getAccessToken());
153+
$this->setRefreshToken($oidc->getRefreshToken());
154+
}
155+
156+
public function getFullApiUrl(string $path = '')
157+
{
158+
// prefix path with default API version if there was no version provided
159+
return implode('/', array_filter([
160+
self::API_URL,
161+
1 === preg_match('/\d\.\d\//', $path) ? '' : self::API_DEFAULT_VERSION,
162+
$path
163+
]));
164+
}
13165

14166
public function get(string $path, array $queryParams = [])
15167
{
@@ -35,4 +187,11 @@ public function patch(string $path, array $data = [], array $queryParams = [])
35187
{
36188
return $this->request($path, self::METHOD_PATCH, $data, $queryParams);
37189
}
190+
191+
abstract protected function request(
192+
string $path = '',
193+
string $method = self::METHOD_GET,
194+
array $data = [],
195+
array $queryParams = []
196+
);
38197
}

src/Bexio/Client.php

Lines changed: 1 addition & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -2,178 +2,13 @@
22
namespace Bexio;
33

44
use Bexio\Exception\BexioClientException;
5-
use Jumbojett\OpenIDConnectClient;
65
use GuzzleHttp\Exception\ClientException;
76
use GuzzleHttp\Client as GuzzleClient;
87

98
class Client extends AbstractClient
109
{
11-
const PROVIDER_URL = 'https://idp.bexio.com';
12-
const API_URL = 'https://api.bexio.com';
13-
const API_DEFAULT_VERSION = '2.0';
14-
15-
/**
16-
* @var array $config
17-
*/
18-
private $config;
19-
20-
/**
21-
* @var string
22-
*/
23-
private $accessToken;
24-
25-
/**
26-
* @var string
27-
*/
28-
private $refreshToken;
29-
30-
/**
31-
* Client constructor.
32-
*
33-
* @param string $clientId
34-
* @param string $clientSecret
35-
* @param string|null $redirectUrl
36-
*/
37-
public function __construct(string $clientId, string $clientSecret, string $redirectUrl = null)
38-
{
39-
$this->config = [
40-
'clientId' => $clientId,
41-
'clientSecret' => $clientSecret,
42-
'redirectUrl' => $redirectUrl,
43-
];
44-
}
45-
46-
public function setClientId($clientId)
47-
{
48-
$this->config['clientId'] = $clientId;
49-
}
50-
51-
public function getClientId()
52-
{
53-
return $this->config['clientId'];
54-
}
55-
56-
public function setClientSecret($clientId)
57-
{
58-
$this->config['clientSecret'] = $clientId;
59-
}
60-
61-
public function getClientSecret()
62-
{
63-
return $this->config['clientSecret'];
64-
}
65-
66-
public function setRedirectUrl($redirectUrl)
67-
{
68-
$this->config['redirectUrl'] = $redirectUrl;
69-
}
70-
71-
public function getRedirectUrl()
72-
{
73-
return $this->config['redirectUrl'];
74-
}
75-
76-
public function setAccessToken(string $accessToken)
77-
{
78-
$this->accessToken = $accessToken;
79-
}
80-
81-
public function getAccessToken()
82-
{
83-
return $this->accessToken;
84-
}
85-
86-
public function setRefreshToken(string $refreshToken)
87-
{
88-
$this->refreshToken = $refreshToken;
89-
}
90-
91-
public function getRefreshToken()
92-
{
93-
return $this->refreshToken;
94-
}
95-
96-
public function persistTokens(string $tokensFile)
97-
{
98-
$result = file_put_contents($tokensFile, json_encode([
99-
'accessToken' => $this->getAccessToken(),
100-
'refreshToken' => $this->getRefreshToken()
101-
]));
102-
return ($result !== false);
103-
}
104-
105-
public function loadTokens(string $tokensFile)
106-
{
107-
if (!file_exists($tokensFile)) {
108-
throw new \Exception('Tokens file not found: ' . $tokensFile);
109-
}
110-
$tokens = json_decode(file_get_contents($tokensFile));
111-
112-
$this->setAccessToken($tokens->accessToken);
113-
$this->setRefreshToken($tokens->refreshToken);
114-
115-
// Refresh access token if it is expired
116-
if ($this->isAccessTokenExpired()) {
117-
$this->refreshToken();
118-
$this->persistTokens($tokensFile);
119-
}
120-
}
121-
122-
/**
123-
* @return OpenIDConnectClient
124-
*/
125-
public function getOpenIDConnectClient()
126-
{
127-
$oidc = new OpenIDConnectClient(
128-
self::PROVIDER_URL,
129-
$this->getClientId(),
130-
$this->getClientSecret()
131-
);
132-
$oidc->setAccessToken($this->accessToken);
133-
return $oidc;
134-
}
135-
136-
public function authenticate($scopes)
137-
{
138-
if (!\is_array($scopes)) {
139-
$scopes = \explode(' ', $scopes);
140-
}
141-
$oidc = $this->getOpenIDConnectClient();
142-
$oidc->setRedirectURL($this->getRedirectUrl());
143-
$oidc->addScope($scopes);
144-
$oidc->authenticate();
145-
146-
$this->setAccessToken($oidc->getAccessToken());
147-
$this->setRefreshToken($oidc->getRefreshToken());
148-
}
149-
150-
public function isAccessTokenExpired($gracePeriod = 30)
151-
{
152-
if (!$this->accessToken) {
153-
return true;
154-
}
155-
$payload = $this->getOpenIDConnectClient()->getAccessTokenPayload();
156-
$expiry = $payload->exp ?? 0;
157-
return time() > ($expiry - $gracePeriod);
158-
}
159-
160-
public function refreshToken()
161-
{
162-
$oidc = $this->getOpenIDConnectClient();
163-
$oidc->refreshToken($this->getRefreshToken());
164-
$this->setAccessToken($oidc->getAccessToken());
165-
$this->setRefreshToken($oidc->getRefreshToken());
166-
}
167-
16810
protected function request(string $path = '', string $method = self::METHOD_GET, array $data = [], array $queryParams = [])
16911
{
170-
// prefix path with default API version if there was no version provided
171-
$apiUrl = implode('/', array_filter([
172-
self::API_URL,
173-
(1 === preg_match('/\d\.\d\//', $path)) ? '' : self::API_DEFAULT_VERSION,
174-
$path
175-
]));
176-
17712
$options = [
17813
'headers' => [
17914
'Authorization' => 'Bearer ' . $this->getAccessToken(),
@@ -191,7 +26,7 @@ protected function request(string $path = '', string $method = self::METHOD_GET,
19126
}
19227

19328
try {
194-
$response = (new GuzzleClient())->request($method, $apiUrl, $options);
29+
$response = (new GuzzleClient())->request($method, $this->getFullApiUrl($path), $options);
19530
} catch (ClientException $e) {
19631
// transform Guzzle ClientException into some more readable form, so that body content does not get truncated
19732
$body = json_decode($e->getResponse()->getBody()->getContents());

0 commit comments

Comments
 (0)