Skip to content

Commit

Permalink
Merge pull request #6 from jdecool/http-psr18
Browse files Browse the repository at this point in the history
PSR-18 compatible HttpClient
  • Loading branch information
jdecool committed Sep 30, 2020
2 parents b190965 + 8ba060e commit a7e5f6d
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 99 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: docker://composer
with:
args: install
- uses: docker://php:7.3-cli-alpine
- uses: docker://php:7.4-cli-alpine
with:
entrypoint: vendor/bin/php-cs-fixer
args: fix -v --dry-run
Expand All @@ -23,7 +23,7 @@ jobs:
- uses: docker://composer
with:
args: install
- uses: docker://php:7.3-cli-alpine
- uses: docker://php:7.4-cli-alpine
with:
entrypoint: vendor/bin/phpstan
args: analyse src -c phpstan.neon -l max
Expand All @@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
version: ['7.2', '7.3']
version: ['7.3', '7.4']
steps:
- uses: actions/checkout@master
- uses: baptouuuu/setup-php@1.0.2
Expand Down
29 changes: 0 additions & 29 deletions .travis.yml

This file was deleted.

76 changes: 15 additions & 61 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,122 +10,76 @@
Exception\BadRequest,
Exception\Forbidden,
Exception\NotFound,
Exception\Unauthorized
Exception\Unauthorized,
};
use Psr\Http\Message\ResponseInterface;
use RuntimeException;

class Client
{
private $http;
private $baseUri;
private $apiKey;

public function __construct(HttpMethodsClient $http, string $baseUri, string $apiKey)
public function __construct(HttpMethodsClient $http)
{
if (false === filter_var($baseUri, FILTER_VALIDATE_URL)) {
throw new RuntimeException('Invalid Clockify endpoint.');
}

$this->http = $http;
$this->baseUri = $baseUri;
$this->apiKey = $apiKey;
}

public function get(string $uri, array $params = []): array
{
$response = $this->http->get(
$this->endpoint($uri, $params),
['X-Api-Key' => $this->apiKey]
);
if (!empty($params)) {
$uri .= '?'.http_build_query($params);
}

$response = $this->http->get($uri);

if (200 !== $response->getStatusCode()) {
throw $this->createExceptionFromResponse($response);
}

return json_decode($response->getBody()->getContents(), true);
return json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
}

public function post(string $uri, array $data): array
{
$response = $this->http->post(
$this->endpoint($uri),
[
'Content-Type' => 'application/json',
'X-Api-Key' => $this->apiKey,
],
json_encode($data)
);
$response = $this->http->post($uri, [], json_encode($data, JSON_THROW_ON_ERROR));

if (201 !== $response->getStatusCode()) {
throw $this->createExceptionFromResponse($response);
}

return json_decode($response->getBody()->getContents(), true);
return json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
}

public function put(string $uri, array $data): array
{
$response = $this->http->put(
$this->endpoint($uri),
[
'Content-Type' => 'application/json',
'X-Api-Key' => $this->apiKey,
],
json_encode($data)
);
$response = $this->http->put($uri, [], json_encode($data, JSON_THROW_ON_ERROR));

if (!in_array($response->getStatusCode(), [200, 201], true)) {
throw $this->createExceptionFromResponse($response);
}

return json_decode($response->getBody()->getContents(), true);
return json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
}

public function patch(string $uri, array $data): array
{
$response = $this->http->patch(
$this->endpoint($uri),
[
'Content-Type' => 'application/json',
'X-Api-Key' => $this->apiKey,
],
json_encode($data)
);
$response = $this->http->patch($uri, [], json_encode($data, JSON_THROW_ON_ERROR));

if (!in_array($response->getStatusCode(), [200, 204], true)) {
throw $this->createExceptionFromResponse($response);
}

return json_decode($response->getBody()->getContents(), true);
return json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
}

public function delete(string $uri): void
{
$response = $this->http->delete(
$this->endpoint($uri)
);
$response = $this->http->delete($uri);

if (204 !== $response->getStatusCode()) {
throw $this->createExceptionFromResponse($response);
}
}

private function endpoint(string $uri, array $params = []): string
{
$endpoint = sprintf(
'%s/%s',
rtrim($this->baseUri, '/'),
ltrim($uri, '/')
);

if (!empty($params)) {
$endpoint .= http_build_query($params);
}

return $endpoint;
}

private function createExceptionFromResponse(ResponseInterface $response): ClockifyException
{
$data = json_decode((string) $response->getBody(), true);
Expand Down
53 changes: 47 additions & 6 deletions src/ClientBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,40 @@

use Http\Client\{
Common\HttpMethodsClient,
Common\Plugin\AddHostPlugin,
Common\Plugin\AddPathPlugin,
Common\Plugin\AuthenticationPlugin,
Common\Plugin\HeaderSetPlugin,
Common\PluginClient,
HttpClient,
};
use Http\Message\MessageFactory;
use Psr\Http\Message\RequestFactoryInterface;
use Http\Message\Authentication\Header;
use Psr\Http\Message\{
RequestFactoryInterface,
UriFactoryInterface,
};
use Http\Discovery\{
Psr17FactoryDiscovery,
Psr18ClientDiscovery,
};
use RuntimeException;

class ClientBuilder
{
private const ENDPOINT_V1 = 'https://api.clockify.me/api/v1/';

private $httpClient;
private $requestFactory;
private $uriFactory;

public function __construct(?HttpClient $httpClient = null, ?RequestFactoryInterface $requestFactory = null)
{
public function __construct(
?HttpClient $httpClient = null,
?RequestFactoryInterface $requestFactory = null,
?UriFactoryInterface $uriFactory = null
) {
$this->httpClient = $httpClient ?? Psr18ClientDiscovery::find();
$this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory();
$this->uriFactory = $uriFactory ?? Psr17FactoryDiscovery::findUriFactory();
}

public function createClientV1(string $apiKey): Client
Expand All @@ -35,8 +49,35 @@ public function createClientV1(string $apiKey): Client

public function create(string $endpoint, string $apiKey): Client
{
$http = new HttpMethodsClient($this->httpClient, $this->requestFactory);
if (false === filter_var($endpoint, FILTER_VALIDATE_URL)) {
throw new RuntimeException('Invalid Clockify endpoint.');
}

if ('' === trim($apiKey)) {
throw new RuntimeException('API token is required.');
}

$plugins = [
new AuthenticationPlugin(
new Header('X-Api-Key', $apiKey),
),
new AddHostPlugin(
$this->uriFactory->createUri($endpoint),
),
new AddPathPlugin(
$this->uriFactory->createUri($endpoint),
),
new HeaderSetPlugin([
'User-Agent' => 'github.com/jdecool/clockify-api',
'Content-Type' => 'application/json',
]),
];

$http = new HttpMethodsClient(
new PluginClient($this->httpClient, $plugins),
$this->requestFactory,
);

return new Client($http, $endpoint, $apiKey);
return new Client($http);
}
}

0 comments on commit a7e5f6d

Please sign in to comment.