Skip to content

Commit

Permalink
Many changes, still deciding how to structure everything
Browse files Browse the repository at this point in the history
  • Loading branch information
ericsizemore committed Dec 24, 2023
1 parent dbb83d8 commit daa18cd
Show file tree
Hide file tree
Showing 21 changed files with 316 additions and 1,272 deletions.
47 changes: 28 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,38 @@ require 'vendor/autoload.php';

## Basic Usage

LibrariesIO splits the different endpoints of the API into several "components":
LibrariesIO splits the different endpoints based on their "component":

* Esi\LibrariesIO\Platform\Platform
* Esi\LibrariesIO\Platform
* Platform::makeRequest() does not require an $endpoint, though you can pass 'platforms'.
* Esi\LibrariesIO\Project
* Esi\LibrariesIO\Project\Contributors
* Esi\LibrariesIO\Project\Dependencies
* Esi\LibrariesIO\Project\Dependents
* Esi\LibrariesIO\Project\DependentRepositories
* Esi\LibrariesIO\Project\Project
* Esi\LibrariesIO\Project\Search
* Esi\LibrariesIO\Project\SourceRank
* Project::makeRequest() takes an 'endpoint' parameter to specify which subset you are looking for.
* Current endpoints are:
* contributors
* dependencies
* dependents
* dependent_repositories
* project
* search
* sourceRank
* Esi\LibrariesIO\Repository
* Esi\LibrariesIO\Repository\Dependencies
* Esi\LibrariesIO\Repository\Projects
* Esi\LibrariesIO\Repository\Repository
* Repository::makeRequest() takes an 'endpoint' parameter to specify which subset you are looking for.
* Current endpoints are:
* dependencies
* projects
* repository
* Esi\LibrariesIO\User
* Esi\LibrariesIO\User\Dependencies
* Esi\LibrariesIO\User\Packages
* Esi\LibrariesIO\User\PackageContributions
* Esi\LibrariesIO\User\Repositories
* Esi\LibrariesIO\User\RepositoryContributions
* Esi\LibrariesIO\User\Subscriptions
* Esi\LibrariesIO\User\User
* User::makeRequest() takes an 'endpoint' parameter to specify which subset you are looking for.
* Current endpoints are:
* dependencies
* packages
* package_contributions
* repositories
* repository_contributions
* subscriptions
* user

Each 'subset' has their own required options. Check the documentation (currently WIP) for more information.

As an example, let's say you want to get a list of the available platforms. To do so:

Expand Down
94 changes: 78 additions & 16 deletions src/AbstractBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,27 @@
namespace Esi\LibrariesIO;

// Exceptions and Attributes
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\{
GuzzleException,
ClientException
};
use InvalidArgumentException, JsonException;
use SensitiveParameter;
use InvalidArgumentException;
use JsonException;

// Guzzle
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Exception\ClientException;
// HTTP
use GuzzleHttp\{
Client,
HandlerStack
};
use Psr\Http\Message\ResponseInterface;

// Cache
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Kevinrob\GuzzleCache\CacheMiddleware;
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
use Kevinrob\GuzzleCache\Storage\Psr6CacheStorage;
use Kevinrob\GuzzleCache\{
CacheMiddleware,
Strategy\PrivateCacheStrategy,
Storage\Psr6CacheStorage
};

// Functions and constants
use function is_dir, is_writable, json_decode, preg_match;
Expand Down Expand Up @@ -73,29 +78,29 @@ abstract class AbstractBase
*
* @var Client
*/
protected Client $client;
public Client $client;

/**
* Base API endpoint.
*
* @var string
*/
protected const API_URL = 'https://libraries.io/api/';
public const API_URL = 'https://libraries.io/api/';

/**
* Libraries.io API key.
*
* @see https://libraries.io/account
* @var ?string
*/
protected ?string $apiKey = null;
public ?string $apiKey = null;

/**
* Path to your cache folder on the file system.
*
* @var ?string
*/
protected ?string $cachePath = null;
public ?string $cachePath = null;

/**
* Constructor.
Expand Down Expand Up @@ -127,7 +132,7 @@ public function __construct(
* @param array<string, int|string> $query
* @return Client
*/
protected function makeClient(?array $query = null): Client
public function makeClient(?array $query = null): Client
{
// Some endpoints do not require any query parameters
if ($query === null) {
Expand Down Expand Up @@ -174,11 +179,68 @@ protected function makeClient(?array $query = null): Client
/**
* Performs the actual request to the API.
*
* @param string $endpoint
* @param array<string, int|string> $options
* @return ResponseInterface
* @throws ClientException|GuzzleException
*/
abstract function makeRequest(array $options): ResponseInterface;
public abstract function makeRequest(string $endpoint, array $options): ResponseInterface;

/**
* Processes the available parameters for a given endpoint.
*
* @param string $endpoint
* @return array<string, array<int, string>|string>
*/
public abstract function endpointParameters(string $endpoint): array;

/**
* Each endpoint class will have a 'subset' of endpoints that fall under it. This
* function handles returning a formatted endpoint for the Client.
*
* @param string $format
* @param array<string, int|string> $options
* @return string
*/
public function processEndpointFormat(string $format, array $options): string
{
if (\str_contains($format, ':') === false) {
return $format;
}

foreach ($options AS $key => $val) {
if ($key === 'page' || $key === 'per_page') {
continue;
}
$format = str_replace(":$key", $val, $format);

Check failure on line 215 in src/AbstractBase.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis 8.3

Parameter #2 $replace of function str_replace expects array|string, int|string given.
}
return $format;
}

/**
* Helper function to make sure that the $options passed to the child class' makeRequest()
* contains the required options listed in the endpoints options.
*
* @param array<string, array<int, string>|string> $endpointOptions
* @param array<string, int|string> $options
* @return bool
*/
public function verifyEndpointOptions(array $endpointOptions, array $options): bool
{
if ($endpointOptions === []) {
return true;
}

$noError = true;

foreach ($endpointOptions AS $endpointOption) {
if (!isset($options[$endpointOption])) {

Check failure on line 237 in src/AbstractBase.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis 8.3

Possibly invalid array key type array<int, string>|string.
$noError = false;
break;
}
}
return $noError;
}

/**
* Decodes the jSON returned from the API. Returns as an associative array.
Expand Down
4 changes: 4 additions & 0 deletions src/Exception/RateLimitExceededException.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

/**
* @psalm-api
*/
final class RateLimitExceededException extends InvalidArgumentException
{
//
Expand Down
32 changes: 26 additions & 6 deletions src/Platform/Platform.php → src/Platform.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
* @copyright (C) 2023 Eric Sizemore
* @license The MIT License (MIT)
*/
namespace Esi\LibrariesIO\Platform;
namespace Esi\LibrariesIO;

use Esi\LibrariesIO\Exception\RateLimitExceededException;
use Esi\LibrariesIO\AbstractBase;
use Esi\LibrariesIO\{
Exception\RateLimitExceededException,
AbstractBase
};

use GuzzleHttp\Exception\ClientException;
use Psr\Http\Message\ResponseInterface;
Expand Down Expand Up @@ -55,16 +57,23 @@ final class Platform extends AbstractBase
/**
* {@inheritdoc}
*/
public function makeRequest(?array $options = null): ResponseInterface
public function makeRequest(string $endpoint = 'platforms', ?array $options = null): ResponseInterface
{
// We actually do not need any $options for this call
// We actually do not need any $options for this call, and the only valid endpoint is 'platforms' currently
$endpointParameters = $this->endpointParameters($endpoint);

if ($endpointParameters === []) {
throw new InvalidArgumentException(

Check failure on line 66 in src/Platform.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis 8.3

Instantiated class Esi\LibrariesIO\InvalidArgumentException not found.

Check failure on line 66 in src/Platform.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis 8.3

Throwing object of an unknown class Esi\LibrariesIO\InvalidArgumentException.
'Invalid endpoint specified. Must be one of: platforms'
);
}

// Build query
parent::makeClient();

// Attempt the request
try {
return $this->client->get('platforms');
return $this->client->get($endpointParameters['format']);

Check failure on line 76 in src/Platform.php

View workflow job for this annotation

GitHub Actions / PHPStan Static Analysis 8.3

Parameter #1 $uri of method GuzzleHttp\Client::get() expects Psr\Http\Message\UriInterface|string, array<int, string>|string given.
} catch (ClientException $e) {
if ($e->getResponse()->getStatusCode() === 429) {
throw new RateLimitExceededException('Libraries.io API rate limit exceeded.', previous: $e);
Expand All @@ -73,4 +82,15 @@ public function makeRequest(?array $options = null): ResponseInterface
}
}
}

/**
* {@inheritdoc}
*/
public function endpointParameters(string $endpoint): array
{
return match($endpoint) {
'platforms' => ['format' => 'platforms', 'options' => []],
default => []
};
}
}
Loading

0 comments on commit daa18cd

Please sign in to comment.