Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 95fb1ef

Browse files
authored
Merge pull request #1 from grixu/dev/3.0
Dev/3.0
2 parents fa1ae98 + 3474391 commit 95fb1ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2358
-552
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to `api-client` will be documented in this file
44

5+
## 3.0.0 - 2021-02-12
6+
7+
- Huge rebuild package both in concept and code layer.
8+
- Uses PHP8, dropped PHP 7.4 compatibility.
9+
- More concentrated on delivery flexible package that could be use directly in apps rather than in other packages
10+
- Made interfaces & classes for configuration, authorization, data fetching, containing & parsing to final classes delivered by user
11+
- Prepared classes for config, url creating & fetching data from JSON API
12+
513
## 2.0.1 - 2021-01-20
614

715
- Updated to be compatible with PHP8

README.md

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# API Client
22

3-
Simple API Client with OAuth2 Auth handler.
3+
Simple API Client with OAuth2 Auth handler.
44

55
## Installation
66

@@ -10,39 +10,79 @@ You can install the package via composer:
1010
composer require grixu/api-client
1111
```
1212

13-
## Usage
13+
## Usage for JSON API
14+
15+
### Create configuration object
16+
17+
```php
18+
use Grixu\ApiClient\Config\JsonApiConfig;
19+
use Grixu\ApiClient\Data\PaginatedData;
20+
use Grixu\ApiClient\Data\StraightKeyParser;
21+
22+
$config = new JsonApiConfig(
23+
baseUrl: 'http://rywal.com.pl',
24+
responseDataClass: PaginatedData::class,
25+
responseParserClass: StraightKeyParser::class,
26+
authType: 'oAuth2', // or you can use enum: AuthType::OAUTH2()
27+
authUrl: 'http://rywal.com.pl',
28+
authData: ['key', 'secret'],
29+
paginationParam: 'page',
30+
filters: ['list', 'of', 'param', 'names', 'that', 'could', 'be', 'used', 'as', 'filters'],
31+
includes: ['same', 'but', 'for', 'includes'],
32+
sorts: ['same', 'this', 'time', 'for', 'sort', 'options']
33+
);
34+
```
35+
36+
If you have various values of filter names, or extensive API to handle - consider creating Factory which will be
37+
handling creating `JsonApiConfig`. Or keep them in separate config file.
38+
39+
### Create fetcher
40+
41+
```php
42+
use Grixu\ApiClient\JsonApiFetcher;
43+
44+
$fetcher = new JsonApiFetcher($config, '/api/path');
45+
```
1446

47+
Here, you can adjust your query using `UrlCompose` by adding filters, sorts, includes:
1548

16-
Then you can just simply call:
49+
```php
50+
// in every example you could pass multiple values
51+
$fetcher->compose()->addFilter('filter_name', 'filter_value_1');
52+
$fetcher->compose()->addInclude('include', 'include_relationship_1', 'include_relationship_2');
53+
$fetcher->compose()->addSort('sort', 'sort_field');
1754

18-
``` php
19-
use Grixu\ApiClient\ApiClient;
55+
//also you could set page in pagination
56+
$fetcher->compose()->setPage('page', 2);
57+
// or simply move to next page by hand
58+
$fetcher->composer->nextPage();
59+
```
2060

21-
$client = ApiClient::make(
22-
'base Url',
23-
'oAuth Url',
24-
'client ID',
25-
'client Key',
26-
'cache key',
27-
);
61+
#### Fetch Data
2862

29-
$response = $client->call('url path like /products');
63+
```php
64+
$fetcher->fetch();
65+
$parsedCollection = $parser->parse(DtoClass::class);
3066
```
3167

32-
### Configuration
68+
`$parsedCollection` is `\Illuminate\Support\Collection` filled with DTOs you
69+
70+
## Configuration
71+
3372
You can adjust global configuration of APIClient in your `.env` file:
73+
3474
```dotenv
3575
API_ERROR_LOGGING=true
3676
API_ERROR_LOG_CHANNEL="api-client"
3777
```
3878

39-
### Testing
79+
## Testing
4080

4181
``` bash
4282
composer test
4383
```
4484

45-
### Changelog
85+
## Changelog
4686

4787
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
4888

composer.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
}
1717
],
1818
"require": {
19-
"php": "^7.4|^8.0",
19+
"php": "^8.0",
2020
"ext-json": "*",
2121
"guzzlehttp/guzzle": "^7.2",
22+
"guzzlehttp/psr7": "^1.7",
2223
"illuminate/http": "^8.0",
23-
"illuminate/support": "^8.0"
24+
"illuminate/support": "^8.0",
25+
"spatie/laravel-enum": "^2.2"
2426
},
2527
"require-dev": {
28+
"spatie/data-transfer-object": "^2.8",
2629
"orchestra/testbench": "^6.0",
2730
"phpunit/phpunit": "^9.4"
2831
},

config/config.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<?php
22

3-
/*
4-
* You can place your custom package configuration in here.
5-
*/
63
return [
4+
'auth_types' => [
5+
\Grixu\ApiClient\Enums\AuthType::OAUTH2()->value => \Grixu\ApiClient\Auth\OAuthToken::class
6+
],
7+
78
'logging' => env('API_ERROR_LOGGING', false),
89
'log_channel' => env('API_ERROR_LOG_CHANNEL', 'api-client'),
910
];

src/AbstractApiFetcher.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Grixu\ApiClient;
4+
5+
use Closure;
6+
use Grixu\ApiClient\Contracts\ApiFetcher;
7+
use Grixu\ApiClient\Contracts\Config;
8+
use Grixu\ApiClient\Contracts\TokenAuth;
9+
use Illuminate\Support\Collection;
10+
11+
abstract class AbstractApiFetcher implements ApiFetcher
12+
{
13+
protected ?TokenAuth $token;
14+
15+
protected Collection $dataCollection;
16+
17+
public function __construct(
18+
private Config $config,
19+
private string $path
20+
) {
21+
$tokenClassName = config('api-client.auth_types')[$config->getAuthType()->value];
22+
$this->token = new $tokenClassName($config);
23+
24+
$this->dataCollection = collect();
25+
}
26+
27+
protected function callClosure(?Closure $closure)
28+
{
29+
if ($closure) {
30+
$closure->call($this);
31+
}
32+
}
33+
34+
public function getDataCollection(): Collection
35+
{
36+
return $this->dataCollection;
37+
}
38+
39+
public function parse(string $dtoClass)
40+
{
41+
$parserClassName = $this->config->getResponseParserClass();
42+
$parser = new $parserClassName($dtoClass);
43+
44+
$flattenData = $this->dataCollection->flatten(1);
45+
return $parser->parse($flattenData);
46+
}
47+
}

src/ApiClient.php

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/ApiClientFacade.php

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/ApiClientServiceProvider.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ public function boot()
1717

1818
public function register()
1919
{
20-
// Automatically apply the package configuration
2120
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'api-client');
22-
23-
// Register the main class to use with the facade
24-
$this->app->bind('api-client', function () {
25-
return new ApiClient;
26-
});
2721
}
2822
}

src/Auth/AuthData.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Grixu\ApiClient\Auth;
4+
5+
class AuthData
6+
{
7+
public function __construct(protected string $key, protected string $secret)
8+
{}
9+
10+
public function getKey(): string
11+
{
12+
return $this->key;
13+
}
14+
15+
public function getSecret(): string
16+
{
17+
return $this->secret;
18+
}
19+
}

src/Auth/OAuthToken.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
namespace Grixu\ApiClient\Auth;
4+
5+
use Grixu\ApiClient\Contracts\Config;
6+
use Grixu\ApiClient\Contracts\TokenAuth;
7+
use Grixu\ApiClient\Enums\AuthType;
8+
use Grixu\ApiClient\Exceptions\InvalidAuthTypeException;
9+
use Grixu\ApiClient\Exceptions\TokenIssueException;
10+
use Illuminate\Http\Client\Response;
11+
use Illuminate\Support\Facades\Cache;
12+
use Illuminate\Support\Facades\Http;
13+
14+
class OAuthToken implements TokenAuth
15+
{
16+
private Config $config;
17+
private ?string $token;
18+
private string $cacheName;
19+
20+
public function __construct(Config $config)
21+
{
22+
$this->config = $config;
23+
$this->validateAuthType();
24+
$this->generateCacheName();
25+
}
26+
27+
protected function validateAuthType()
28+
{
29+
if ($this->config->getAuthType() != AuthType::OAUTH2()) {
30+
throw new InvalidAuthTypeException();
31+
}
32+
}
33+
34+
private function generateCacheName(): void
35+
{
36+
$this->cacheName = md5($this->config->getAuthUrl() . $this->config->getAuthType()->value);
37+
}
38+
39+
public function getToken(): string
40+
{
41+
if (empty($this->token)) {
42+
$this->authorize();
43+
}
44+
45+
return $this->token;
46+
}
47+
48+
public function authorize(): void
49+
{
50+
try {
51+
$this->getTokenFromCache();
52+
} catch (\Exception) {
53+
$this->getTokenFromAuthServer();
54+
}
55+
}
56+
57+
private function getTokenFromCache(): void
58+
{
59+
$this->token = Cache::get($this->cacheName);
60+
61+
if (empty($this->token)) {
62+
throw new \Exception('Empty cache');
63+
}
64+
}
65+
66+
private function getTokenFromAuthServer(): void
67+
{
68+
$tokenResponse = $this->makeAuthRequest();
69+
$this->validateAuthResponse($tokenResponse);
70+
71+
$this->token = $tokenResponse->json('access_token');
72+
Cache::put($this->cacheName, $this->token);
73+
}
74+
75+
private function makeAuthRequest(): Response
76+
{
77+
return Http::withHeaders(
78+
[
79+
'Accept' => 'application/json'
80+
]
81+
)
82+
->post(
83+
$this->config->getAuthUrl(),
84+
[
85+
'grant_type' => 'client_credentials',
86+
'client_id' => $this->config->getAuthData()->getKey(),
87+
'client_secret' => $this->config->getAuthData()->getSecret(),
88+
'scope' => '*',
89+
]
90+
);
91+
}
92+
93+
private function validateAuthResponse(Response $authResponse): void
94+
{
95+
if (!$authResponse->successful()) {
96+
throw new TokenIssueException($authResponse->body());
97+
}
98+
}
99+
100+
public function reset(): void
101+
{
102+
Cache::forget($this->cacheName);
103+
$this->getTokenFromAuthServer();
104+
}
105+
}

0 commit comments

Comments
 (0)