From 36cab232b6f59f93ae7bb86988788fd8c5c4380d Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 7 Apr 2019 22:38:00 +0200 Subject: [PATCH] Implemented Playernames to UUIDs endpoint --- CHANGELOG.md | 1 + composer.json | 1 + composer.lock | 5 +++-- src/Api.php | 45 +++++++++++++++++++++++++++++++++++++++++++++ tests/ApiTest.php | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ddd90d..5c76d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `\Ely\Mojang\Api::setClient()` method to override default HTTP client. - [API Status](https://wiki.vg/Mojang_API#API_Status) endpoint. - [UUID to Name history](https://wiki.vg/Mojang_API#UUID_-.3E_Name_history) endpoint. +- [Playernames -> UUIDs](https://wiki.vg/Mojang_API#Playernames_-.3E_UUIDs) endpoint. ### Changed - The constructor no longer has arguments. diff --git a/composer.json b/composer.json index 0cfb0a2..6fe546e 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "require": { "php": ">=7.1.0", "ext-json": "*", + "ext-mbstring": "*", "guzzlehttp/guzzle": "^6.0.0", "ramsey/uuid": "^3.0.0" }, diff --git a/composer.lock b/composer.lock index df09b33..0431217 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": "05e8f182a95a81fa9bd6958b1ac3cb62", + "content-hash": "d651db5f2feb503f674fb070cbfeed7c", "packages": [ { "name": "guzzlehttp/guzzle", @@ -2980,7 +2980,8 @@ "prefer-lowest": false, "platform": { "php": ">=7.1.0", - "ext-json": "*" + "ext-json": "*", + "ext-mbstring": "*" }, "platform-dev": [] } diff --git a/src/Api.php b/src/Api.php index 99a1f80..4b4e3c0 100644 --- a/src/Api.php +++ b/src/Api.php @@ -12,6 +12,7 @@ use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Uri; +use InvalidArgumentException; use Ramsey\Uuid\Uuid; class Api { @@ -132,6 +133,50 @@ class Api { return $this->uuidToTextures($this->usernameToUUID($username)->getId()); } + /** + * @param string[] $names list of users' names + * + * @return \Ely\Mojang\Response\ProfileInfo[] response array is indexed with the initial username case + * + * @throws \Ely\Mojang\Exception\MojangApiException + * @throws \GuzzleHttp\Exception\GuzzleException + * + * @url https://wiki.vg/Mojang_API#Playernames_-.3E_UUIDs + */ + public function playernamesToUuids(array $names): array { + foreach ($names as $i => $name) { + if (empty($name)) { + unset($names[$i]); + } + } + + if (count($names) > 100) { + throw new InvalidArgumentException('You cannot request more than 100 names per request'); + } + + $response = $this->getClient()->request('POST', 'https://authserver.mojang.com/authenticate', [ + 'json' => array_values($names), + ]); + $body = $this->decode($response->getBody()->getContents()); + + $result = []; + foreach ($body as $record) { + $object = Response\ProfileInfo::createFromResponse($record); + $key = $object->getName(); + foreach ($names as $i => $name) { + if (mb_strtolower($name) === mb_strtolower($object->getName())) { + unset($names[$i]); + $key = $name; + break; + } + } + + $result[$key] = $object; + } + + return $result; + } + /** * @param string $login * @param string $password diff --git a/tests/ApiTest.php b/tests/ApiTest.php index f0724ec..fd32f41 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -9,6 +9,7 @@ use Ely\Mojang\Middleware\ResponseConverterMiddleware; use Ely\Mojang\Middleware\RetryMiddleware; use Ely\Mojang\Response\ApiStatus; use Ely\Mojang\Response\NameHistoryItem; +use Ely\Mojang\Response\ProfileInfo; use Ely\Mojang\Response\Properties\TexturesProperty; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; @@ -16,6 +17,7 @@ use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; use GuzzleHttp\Psr7\Response; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use function GuzzleHttp\Psr7\parse_query; @@ -203,6 +205,43 @@ class ApiTest extends TestCase { $this->assertSame('http://textures.minecraft.net/texture/capePath', $textures->getCape()->getUrl()); } + public function testPlayernamesToUuids() { + $this->mockHandler->append($this->createResponse(200, [ + [ + 'id' => '86f6e3695b764412a29820cac1d4d0d6', + 'name' => 'MockUsername', + 'legacy' => false, + 'demo' => true, + ], + ])); + + $result = $this->api->playernamesToUuids([null, '', 'mockusername', 'nonexists']); + + /** @var \Psr\Http\Message\RequestInterface $request */ + $request = $this->history[0]['request']; + $body = json_decode($request->getBody()->getContents(), true); + $this->assertSame(['mockusername', 'nonexists'], $body); + + $this->assertCount(1, $result); + $this->assertContainsOnlyInstancesOf(ProfileInfo::class, $result); + $this->assertArrayHasKey('mockusername', $result); + $this->assertSame('86f6e3695b764412a29820cac1d4d0d6', $result['mockusername']->getId()); + $this->assertSame('MockUsername', $result['mockusername']->getName()); + $this->assertFalse($result['mockusername']->isLegacy()); + $this->assertTrue($result['mockusername']->isDemo()); + } + + public function testPlayernamesToUuidsInvalidArgumentException() { + $names = []; + for ($i = 0; $i < 101; $i++) { + $names[] = base64_encode(random_bytes(4)); + } + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('You cannot request more than 100 names per request'); + $this->api->playernamesToUuids($names); + } + public function testUsernameToTextures() { $this->mockHandler->append($this->createResponse(200, [ 'id' => '86f6e3695b764412a29820cac1d4d0d6',