Skip to content

Commit

Permalink
[wip] Refactor to use Guzzle and custom Response class
Browse files Browse the repository at this point in the history
  • Loading branch information
sprain committed Sep 21, 2023
1 parent 8f99090 commit ee1d489
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 69 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"type": "library",
"require": {
"php": ">=8.1",
"kriswallsmith/buzz": "^0.15.0"
"guzzlehttp/guzzle": "^7.8"
},
"license": "MIT",
"authors": [
Expand Down
83 changes: 83 additions & 0 deletions lib/ApiClient/Http/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

namespace Ticketpark\ApiClient\Http;

use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Psr7\Response as GuzzleResponse;

final class Client
{
private GuzzleClient $guzzle;

public function __construct()
{
$this->guzzle = new GuzzleClient();
}

public function head(string $url, array $headers): Response
{
return $this->execute('head', $url, $headers);
}

public function get(string $url, array $headers): Response
{
return $this->execute('get', $url, $headers);
}

public function post(string $url, string $content, array $headers): Response
{
return $this->execute('post', $url, $headers, $content);
}

public function postForm(string $url, array $formData, array $headers): Response
{
return $this->execute('post', $url, $headers, null, $formData);
}

public function patch(string $url, string $content, array $headers): Response
{
return $this->execute('patch', $url, $headers, $content);
}

public function delete(string $url, array $headers): Response
{
return $this->execute('delete', $url, $headers);
}

private function execute(
string $method,
string $url,
array $headers = [],
string $content = null,
array $formData = []
): Response {
try {
/** @var GuzzleResponse $response */
$guzzleResponse = $this->guzzle->request(
$method,
$url,
[
'headers' => $headers,
'body' => $content,
'form_params' => $formData
]
);
} catch (\Exception $e) {
if (!$e instanceof ClientException) {
throw new HttpRequestException($e->getMessage());
}

/** @var GuzzleResponse $response */
$guzzleResponse = $e->getResponse();
}

return new Response(
$guzzleResponse->getStatusCode(),
(string) $guzzleResponse->getBody(),
$guzzleResponse->getHeaders()
);
}
}
9 changes: 9 additions & 0 deletions lib/ApiClient/Http/HttpRequestException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Ticketpark\ApiClient\Http;

class HttpRequestException extends \Exception
{
}
36 changes: 36 additions & 0 deletions lib/ApiClient/Http/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Ticketpark\ApiClient\Http;

class Response
{
public function __construct(
private int $statusCode,
private string $content,
private array $headers
)
{
}

public function getStatusCode(): int
{
return $this->statusCode;
}

public function getContent(): array
{
return json_decode($this->content, true);
}

public function getHeaders(): array
{
return $this->headers;
}

public function isSuccessful(): bool
{
return ($this->statusCode >= 200 && $this->statusCode <= 204);
}
}
143 changes: 75 additions & 68 deletions lib/ApiClient/TicketparkApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

namespace Ticketpark\ApiClient;

use Buzz\Browser;
use Buzz\Client\Curl;
use Buzz\Message\Response;
use Ticketpark\ApiClient\Http\Client;
use Ticketpark\ApiClient\Http\Response;
use Ticketpark\ApiClient\Exception\TokenGenerationException;
use Ticketpark\ApiClient\Token\AccessToken;
use Ticketpark\ApiClient\Token\RefreshToken;
Expand All @@ -14,9 +13,9 @@ class TicketparkApiClient
private const ROOT_URL = 'https://api.ticketpark.ch';
private const REFRESH_TOKEN_LIFETIME = 30 * 86400;

private ?Client $client = null;
private ?string $username = null;
private ?string $password = null;
private ?Browser $browser = null;
private ?RefreshToken $refreshToken = null;
private ?AccessToken $accessToken = null;

Expand All @@ -26,20 +25,6 @@ public function __construct(
) {
}

public function setBrowser(Browser $browser = null): void
{
$this->browser = $browser;
}

public function getBrowser(): Browser
{
if (null === $this->browser) {
$this->browser = new Browser(new Curl());
}

return $this->browser;
}

public function setUserCredentials(string $username, string $password): void
{
$this->username = $username;
Expand All @@ -51,11 +36,6 @@ public function getAccessToken(): ?AccessToken
return $this->accessToken;
}

public function setAccessTokenInstance(AccessToken $accessToken): void
{
$this->accessToken = $accessToken;
}

public function setAccessToken(string $accessToken): void
{
$this->accessToken = new AccessToken($accessToken);
Expand All @@ -66,49 +46,51 @@ public function getRefreshToken(): ?RefreshToken
return $this->refreshToken;
}

public function setRefreshTokenInstance(RefreshToken $refreshToken): void
{
$this->refreshToken = $refreshToken;
}

public function setRefreshToken(string $refreshToken): void
{
$this->refreshToken = new RefreshToken($refreshToken);
}

public function get(string $path, array $parameters = [], array $headers = []): Response
public function head(string $path, array $parameters = []): Response
{
$params = '';
if (count($parameters)) {
$params = '?' . http_build_query($parameters);
}

return $this->getBrowser()->get(self::ROOT_URL . $path . $params, $this->getDefaultHeaders($headers));
return $this->getClient()->head(
$this->getUrl($path, $parameters),
$this->getHeaders()
);
}

public function post(string $path, mixed $content = '', array $headers = []): Response
public function get(string $path, array $parameters = []): Response
{
return $this->getBrowser()->post(self::ROOT_URL . $path, $this->getDefaultHeaders($headers), json_encode($content, JSON_THROW_ON_ERROR));
return $this->getClient()->get(
$this->getUrl($path, $parameters),
$this->getHeaders()
);
}

public function head($path, array $parameters = [], array $headers = []): Response
public function post(string $path, array $data = []): Response
{
$params = '';
if (count($parameters)) {
$params = '?' . http_build_query($parameters);
}

return $this->getBrowser()->head(self::ROOT_URL . $path . $params, $this->getDefaultHeaders($headers));
return $this->getClient()->post(
$this->getUrl($path),
json_encode($data, JSON_THROW_ON_ERROR),
$this->getHeaders()
);
}

public function patch(string $path, mixed $content = '', array $headers = []): Response
public function patch(string $path, array $data = []): Response
{
return $this->getBrowser()->patch(self::ROOT_URL . $path, $this->getDefaultHeaders($headers), json_encode($content, JSON_THROW_ON_ERROR));
return $this->getClient()->patch(
$this->getUrl($path),
json_encode($data, JSON_THROW_ON_ERROR),
$this->getHeaders()
);
}

public function delete(string $path, array $headers = []): Response
public function delete(string $path): Response
{
return $this->getBrowser()->delete(self::ROOT_URL . $path, $this->getDefaultHeaders($headers));
return $this->getClient()->delete(
$this->getUrl($path),
$this->getHeaders()
);
}

public function generateTokens(): void
Expand All @@ -127,7 +109,7 @@ public function generateTokens(): void
}

// Try with user credentials
if (!isset($data) && $this->username) {
if ($this->username) {
$data = [
'username' => $this->username,
'password' => $this->password,
Expand All @@ -142,51 +124,76 @@ public function generateTokens(): void
throw new TokenGenerationException('Failed to generate a access tokens. Make sure to provide a valid refresh token or user credentials.');
}

private function getDefaultHeaders(array $customHeaders = []): array
private function getClient(): Client
{
$headers = ['Content-Type' => 'application/json', 'Accept' => 'application/json', 'Authorization' => 'Bearer ' . $this->getValidAccessToken()];
if (null === $this->client) {
$this->client = new Client();
}

return array_merge($customHeaders, $headers);
return $this->client;
}

private function getUrl(string $path, array $parameters = []): string
{
$params = '';
if (count($parameters)) {
$params = '?' . http_build_query($parameters);
}

return self::ROOT_URL . $path . $params;
}

private function getValidAccessToken(): string
{
$accessToken = $this->getAccessToken();

if (!$accessToken || $accessToken->hasExpired()) {
if (null === $accessToken || $accessToken->hasExpired()) {
$this->generateTokens();
$accessToken = $this->getAccessToken();
}

return $accessToken->getToken();
}

protected function doGenerateTokens(array $data): bool
private function doGenerateTokens(array $data): bool
{
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
'Authorization' => 'Basic '.base64_encode($this->apiKey . ':' . $this->apiSecret)
];

$response = $this->getBrowser()->post(self::ROOT_URL . '/oauth/v2/token', $headers, $data);
$response = $this->getClient()->postForm(
$this->getUrl('/oauth/v2/token'),
$data,
$headers,
);

if (200 == $response->getStatusCode()) {
$response = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR);
if (!$response->isSuccessful()) {
return false;
}

$this->accessToken = new AccessToken(
$response['access_token'],
(new \DateTime())->setTimestamp(time() + $response['expires_in'])
);
$content = $response->getContent();

$this->refreshToken = new RefreshToken(
$response['refresh_token'],
(new \DateTime())->setTimestamp(time() + self::REFRESH_TOKEN_LIFETIME)
);
$this->accessToken = new AccessToken(
$content['access_token'],
(new \DateTime())->setTimestamp(time() + $content['expires_in'])
);

return true;
}
$this->refreshToken = new RefreshToken(
$content['refresh_token'],
(new \DateTime())->setTimestamp(time() + self::REFRESH_TOKEN_LIFETIME)
);

return false;
return true;
}

private function getHeaders(): array
{
return [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $this->getValidAccessToken()
];
}
}

0 comments on commit ee1d489

Please sign in to comment.