diff --git a/composer.json b/composer.json index 71272a2..9af1771 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "homepage": "https://github.com/aternosorg/php-modrinth-api", "require": { "php": "^8.4.1", + "composer-runtime-api": "^2.0", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", diff --git a/composer.lock b/composer.lock index 3c517fd..24fba47 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "693555b2f9c5b0d64c927f84b5f338c5", + "content-hash": "161653896c078b26e02472c96d5b0454", "packages": [ { "name": "guzzlehttp/guzzle", @@ -2624,6 +2624,7 @@ "prefer-lowest": false, "platform": { "php": "^8.4.1", + "composer-runtime-api": "^2.0", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*" diff --git a/src/Client/ModrinthAPIClient.php b/src/Client/ModrinthAPIClient.php index 565d307..434ef68 100644 --- a/src/Client/ModrinthAPIClient.php +++ b/src/Client/ModrinthAPIClient.php @@ -47,6 +47,8 @@ use Aternos\ModrinthApi\Model\User as UserModel; use Aternos\ModrinthApi\Model\UserPayoutHistory; use Aternos\ModrinthApi\Model\Version as VersionModel; +use Composer\InstalledVersions; +use OutOfBoundsException; use Psr\Http\Client\ClientInterface; @@ -58,6 +60,11 @@ */ class ModrinthAPIClient { + /** + * Name of this package on packagist, used to look up the installed version + */ + public const string PACKAGE_NAME = "aternos/modrinth-api"; + protected ?ClientInterface $httpClient; protected Configuration $configuration; @@ -92,10 +99,34 @@ public function __construct(?string $apiToken = null, ?Configuration $configurat { $this->httpClient = $httpClient; $this->configuration = $configuration ?? (Configuration::getDefaultConfiguration()) - ->setUserAgent("php-modrinth-api/2.0.0"); + ->setUserAgent(static::getDefaultUserAgent()); $this->setApiToken($apiToken); } + /** + * Get the installed version of this library, e.g. "4.0.0" or "dev-master" + * Returns "unknown" if the version cannot be determined, e.g. if the library was not installed using composer. + * @return string + */ + public static function getLibraryVersion(): string + { + try { + return InstalledVersions::getPrettyVersion(static::PACKAGE_NAME) ?? "unknown"; + } catch (OutOfBoundsException) { + return "unknown"; + } + } + + /** + * Get the user agent that identifies this library, e.g. "aternos/php-modrinth-api/4.0.0" + * It is used unless a different user agent is set with {@see setUserAgent()}. + * @return string + */ + public static function getDefaultUserAgent(): string + { + return "aternos/php-modrinth-api/" . static::getLibraryVersion(); + } + /** * @param Configuration $configuration * @return $this diff --git a/tests/Integration/Client/ClientTest.php b/tests/Integration/Client/ClientTest.php index 27942a3..d9af96e 100644 --- a/tests/Integration/Client/ClientTest.php +++ b/tests/Integration/Client/ClientTest.php @@ -37,7 +37,7 @@ class ClientTest extends TestCase public function setUp(): void { $this->apiClient = new ModrinthAPIClient(); - $this->apiClient->setUserAgent("aternos/php-modrinth-api@2.0.0 (contact@aternos.org)"); + $this->apiClient->setUserAgent(ModrinthAPIClient::getDefaultUserAgent() . " (contact@aternos.org)"); } protected function assertValidProjectList($list): void diff --git a/tests/Unit/Client/ModrinthAPIClientTest.php b/tests/Unit/Client/ModrinthAPIClientTest.php index a260c25..63188f8 100644 --- a/tests/Unit/Client/ModrinthAPIClientTest.php +++ b/tests/Unit/Client/ModrinthAPIClientTest.php @@ -14,9 +14,11 @@ use Aternos\ModrinthApi\Client\User; use Aternos\ModrinthApi\Configuration; use Aternos\ModrinthApi\Model\UserPayoutHistory; +use Composer\InstalledVersions; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; +use GuzzleHttp\Middleware; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\Attributes\DataProvider; @@ -31,7 +33,35 @@ public function testConstructorUsesDefaultConfiguration(): void $client = new ModrinthAPIClient(); $this->assertInstanceOf(ModrinthAPIClient::class, $client); - $this->assertStringStartsWith("php-modrinth-api/", Configuration::getDefaultConfiguration()->getUserAgent()); + $this->assertEquals(ModrinthAPIClient::getDefaultUserAgent(), Configuration::getDefaultConfiguration()->getUserAgent()); + } + + public function testGetLibraryVersionReturnsInstalledVersion(): void + { + $this->assertEquals( + InstalledVersions::getPrettyVersion(ModrinthAPIClient::PACKAGE_NAME), + ModrinthAPIClient::getLibraryVersion() + ); + $this->assertNotEquals("unknown", ModrinthAPIClient::getLibraryVersion()); + } + + public function testDefaultUserAgentContainsPackageAndVersion(): void + { + $this->assertEquals( + "aternos/php-modrinth-api/" . ModrinthAPIClient::getLibraryVersion(), + ModrinthAPIClient::getDefaultUserAgent() + ); + } + + public function testDefaultUserAgentIsSentWithRequests(): void + { + $responses = [$this->fixtureResponse("get_project_response")]; + $stack = HandlerStack::create(new MockHandler($responses)); + $stack->push(Middleware::history($this->requests)); + $client = new ModrinthAPIClient(null, null, new Client(['handler' => $stack])); + + $client->getProject("modmenu"); + $this->assertEquals(ModrinthAPIClient::getDefaultUserAgent(), $this->getRequest()->getHeaderLine("User-Agent")); } /** @@ -52,7 +82,7 @@ public function testClientsWithoutExplicitConfigurationShareOne(): void // reset the shared configuration so that it cannot affect other tests Configuration::getDefaultConfiguration() - ->setUserAgent("php-modrinth-api/2.0.0") + ->setUserAgent(ModrinthAPIClient::getDefaultUserAgent()) ->setApiKey("Authorization", null); }