Start of work to replace Guzzle with Artax
This commit is contained in:
parent
02838c5024
commit
deecb5a912
39
src/API/APIClient.php
Normal file
39
src/API/APIClient.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package AnimeListClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2017 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API;
|
||||||
|
|
||||||
|
use Amp;
|
||||||
|
use Amp\Artax\{
|
||||||
|
Client,
|
||||||
|
Response,
|
||||||
|
Request
|
||||||
|
}
|
||||||
|
|
||||||
|
class APIClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a syncronous response for a request
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
static public function syncResponse(Request $request): Response
|
||||||
|
{
|
||||||
|
$client = new Client();
|
||||||
|
return wait($client->request($request));
|
||||||
|
}
|
||||||
|
}
|
@ -68,15 +68,17 @@ class APIRequestBuilder {
|
|||||||
protected $request;
|
protected $request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set body as form fields
|
* Set a basic authentication header
|
||||||
*
|
*
|
||||||
* @param array $fields Mapping of field names to values
|
* @param string $username
|
||||||
|
* @param string $password
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function setFormFields(array $fields): self
|
public function setBasicAuth(string $username, string $password): self
|
||||||
{
|
{
|
||||||
$body = $this->fixBody((new FormBody)->addFields($createData));
|
$authString = 'Basic ' . base64_encode($username . ':' . $password);
|
||||||
$this->setBody($body);
|
$this->setHeader('Authorization', $authString);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +94,20 @@ class APIRequestBuilder {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set body as form fields
|
||||||
|
*
|
||||||
|
* @param array $fields Mapping of field names to values
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setFormFields(array $fields): self
|
||||||
|
{
|
||||||
|
$this->setHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
$body = $this->fixBody((new FormBody)->addFields($fields));
|
||||||
|
$this->setBody($body);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a request header
|
* Set a request header
|
||||||
*
|
*
|
||||||
@ -143,6 +159,16 @@ class APIRequestBuilder {
|
|||||||
public function getFullRequest()
|
public function getFullRequest()
|
||||||
{
|
{
|
||||||
$this->buildUri();
|
$this->buildUri();
|
||||||
|
|
||||||
|
if ($this->logger)
|
||||||
|
{
|
||||||
|
$this->logger->debug('API Request', [
|
||||||
|
'request_url' => $this->request->getUri(),
|
||||||
|
'request_headers' => $this->request->getAllHeaders(),
|
||||||
|
'request_body' => $this->request->getBody()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->request;
|
return $this->request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +192,13 @@ class APIRequestBuilder {
|
|||||||
->setMethod($type)
|
->setMethod($type)
|
||||||
->setProtocol('1.1');
|
->setProtocol('1.1');
|
||||||
|
|
||||||
|
$this->path = $uri;
|
||||||
|
|
||||||
|
if ( ! empty($this->defaultHeaders))
|
||||||
|
{
|
||||||
|
$this->setHeaders($this->defaultHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +211,7 @@ class APIRequestBuilder {
|
|||||||
{
|
{
|
||||||
$url = (strpos($this->path, '//') !== FALSE)
|
$url = (strpos($this->path, '//') !== FALSE)
|
||||||
? $this->path
|
? $this->path
|
||||||
: $this->baseUrl . $url;
|
: $this->baseUrl . $this->path;
|
||||||
|
|
||||||
if ( ! empty($this->query))
|
if ( ! empty($this->query))
|
||||||
{
|
{
|
||||||
|
@ -37,30 +37,35 @@ class ListItem extends AbstractListItem {
|
|||||||
|
|
||||||
public function create(array $data): bool
|
public function create(array $data): bool
|
||||||
{
|
{
|
||||||
$response = $this->getResponse('POST', 'library-entries', [
|
$body = [
|
||||||
'body' => Json::encode([
|
'data' => [
|
||||||
'data' => [
|
'type' => 'libraryEntries',
|
||||||
'type' => 'libraryEntries',
|
'attributes' => [
|
||||||
'attributes' => [
|
'status' => $data['status'],
|
||||||
'status' => $data['status'],
|
'progress' => $data['progress'] ?? 0
|
||||||
'progress' => $data['progress'] ?? 0
|
],
|
||||||
|
'relationships' => [
|
||||||
|
'user' => [
|
||||||
|
'data' => [
|
||||||
|
'id' => $data['user_id'],
|
||||||
|
'type' => 'users'
|
||||||
|
]
|
||||||
],
|
],
|
||||||
'relationships' => [
|
'media' => [
|
||||||
'user' => [
|
'data' => [
|
||||||
'data' => [
|
'id' => $data['id'],
|
||||||
'id' => $data['user_id'],
|
'type' => $data['type']
|
||||||
'type' => 'users'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'media' => [
|
|
||||||
'data' => [
|
|
||||||
'id' => $data['id'],
|
|
||||||
'type' => $data['type']
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
])
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$request = $this->requestBuilder->newRequest('POST', 'library-entries')
|
||||||
|
->setJsonBody($body)
|
||||||
|
->getFullRequest();
|
||||||
|
$response = $this->getResponse('POST', 'library-entries', [
|
||||||
|
'body' => Json::encode($body)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return ($response->getStatusCode() === 201);
|
return ($response->getStatusCode() === 201);
|
||||||
@ -74,11 +79,19 @@ class ListItem extends AbstractListItem {
|
|||||||
|
|
||||||
public function get(string $id): array
|
public function get(string $id): array
|
||||||
{
|
{
|
||||||
return $this->getRequest("library-entries/{$id}", [
|
$request = $this->requestBuilder->newRequest('GET', "library-entries/{$id}")
|
||||||
|
->setQuery([
|
||||||
|
'include' => 'media,media.genres,media.mappings'
|
||||||
|
])
|
||||||
|
->getFullRequest();
|
||||||
|
/*return $this->getRequest("library-entries/{$id}", [
|
||||||
'query' => [
|
'query' => [
|
||||||
'include' => 'media,media.genres,media.mappings'
|
'include' => 'media,media.genres,media.mappings'
|
||||||
]
|
]
|
||||||
]);
|
]);*/
|
||||||
|
|
||||||
|
$response = \Amp\wait((new \Amp\Artax\Client)->request($request));
|
||||||
|
return Json::decode($response->getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(string $id, array $data): Response
|
public function update(string $id, array $data): Response
|
||||||
|
@ -116,7 +116,6 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'mal_id' => $item['mal_id'] ?? null,
|
'mal_id' => $item['mal_id'] ?? null,
|
||||||
'data' => [
|
'data' => [
|
||||||
'status' => $item['watching_status'],
|
'status' => $item['watching_status'],
|
||||||
'rating' => $item['user_rating'] / 2,
|
|
||||||
'reconsuming' => $rewatching,
|
'reconsuming' => $rewatching,
|
||||||
'reconsumeCount' => $item['rewatched'],
|
'reconsumeCount' => $item['rewatched'],
|
||||||
'notes' => $item['notes'],
|
'notes' => $item['notes'],
|
||||||
@ -125,9 +124,9 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if ((int) $untransformed['data']['rating'] === 0)
|
if ( ! empty($item['user_rating']))
|
||||||
{
|
{
|
||||||
unset($untransformed['data']['rating']);
|
$untransformed['data']['rating'] = $item['user_rating'] / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $untransformed;
|
return $untransformed;
|
||||||
|
@ -30,7 +30,7 @@ class ListItem {
|
|||||||
use ContainerAware;
|
use ContainerAware;
|
||||||
use MALTrait;
|
use MALTrait;
|
||||||
|
|
||||||
public function create(array $data): bool
|
public function create(array $data)
|
||||||
{
|
{
|
||||||
$id = $data['id'];
|
$id = $data['id'];
|
||||||
$createData = [
|
$createData = [
|
||||||
@ -40,11 +40,20 @@ class ListItem {
|
|||||||
])
|
])
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// $config = $this->container->get('config');
|
||||||
|
|
||||||
|
/*$request = $this->requestBuilder->newRequest('POST', "animelist/add/{$id}.xml")
|
||||||
|
->setFormFields($createData)
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||||
|
->getFullRequest();*/
|
||||||
|
|
||||||
$response = $this->getResponse('POST', "animelist/add/{$id}.xml", [
|
$response = $this->getResponse('POST', "animelist/add/{$id}.xml", [
|
||||||
'body' => $this->fixBody((new FormBody)->addFields($createData))
|
'body' => $this->fixBody((new FormBody)->addFields($createData))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $response->getBody() === 'Created';
|
return $response->getBody() === 'Created';
|
||||||
|
|
||||||
|
// return $request;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(string $id): bool
|
public function delete(string $id): bool
|
||||||
|
@ -89,47 +89,23 @@ trait MALTrait {
|
|||||||
*/
|
*/
|
||||||
public function setUpRequest(string $type, string $url, array $options = [])
|
public function setUpRequest(string $type, string $url, array $options = [])
|
||||||
{
|
{
|
||||||
$this->defaultHeaders['User-Agent'] = $_SERVER['HTTP_USER_AGENT'] ?? $this->defaultHeaders;
|
|
||||||
|
|
||||||
$type = strtoupper($type);
|
|
||||||
$validTypes = ['GET', 'POST', 'DELETE'];
|
|
||||||
|
|
||||||
if ( ! in_array($type, $validTypes))
|
|
||||||
{
|
|
||||||
throw new InvalidArgumentException('Invalid http request type');
|
|
||||||
}
|
|
||||||
|
|
||||||
$config = $this->container->get('config');
|
$config = $this->container->get('config');
|
||||||
$logger = $this->container->getLogger('mal-request');
|
|
||||||
|
|
||||||
$headers = array_merge($this->defaultHeaders, $options['headers'] ?? [], [
|
$request = $this->requestBuilder
|
||||||
'Authorization' => 'Basic ' .
|
->newRequest($type, $url)
|
||||||
base64_encode($config->get(['mal','username']) . ':' .$config->get(['mal','password']))
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal','password']));
|
||||||
]);
|
|
||||||
|
|
||||||
$query = $options['query'] ?? [];
|
if (array_key_exists('query', $options))
|
||||||
|
|
||||||
$url = (strpos($url, '//') !== FALSE)
|
|
||||||
? $url
|
|
||||||
: $this->baseUrl . $url;
|
|
||||||
|
|
||||||
if ( ! empty($query))
|
|
||||||
{
|
{
|
||||||
$url .= '?' . http_build_query($query);
|
$request->setQuery($options['query']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$request = (new Request)
|
|
||||||
->setMethod($type)
|
|
||||||
->setUri($url)
|
|
||||||
->setProtocol('1.1')
|
|
||||||
->setAllHeaders($headers);
|
|
||||||
|
|
||||||
if (array_key_exists('body', $options))
|
if (array_key_exists('body', $options))
|
||||||
{
|
{
|
||||||
$request->setBody($options['body']);
|
$request->setBody($options['body']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $request;
|
return $request->getFullRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,15 +127,12 @@ trait MALTrait {
|
|||||||
$request = $this->setUpRequest($type, $url, $options);
|
$request = $this->setUpRequest($type, $url, $options);
|
||||||
$response = \Amp\wait((new Client)->request($request));
|
$response = \Amp\wait((new Client)->request($request));
|
||||||
|
|
||||||
$logger->debug('MAL api request', [
|
$logger->debug('MAL api response', [
|
||||||
'url' => $url,
|
|
||||||
'status' => $response->getStatus(),
|
'status' => $response->getStatus(),
|
||||||
'reason' => $response->getReason(),
|
'reason' => $response->getReason(),
|
||||||
|
'body' => $response->getBody(),
|
||||||
'headers' => $response->getAllHeaders(),
|
'headers' => $response->getAllHeaders(),
|
||||||
'requestHeaders' => $request->getAllHeaders(),
|
'requestHeaders' => $request->getAllHeaders(),
|
||||||
'requestBody' => $request->hasBody() ? $request->getBody() : 'No request body',
|
|
||||||
'requestBodyBeforeEncode' => $request->hasBody() ? urldecode($request->getBody()) : '',
|
|
||||||
'body' => $response->getBody()
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
|
@ -57,26 +57,32 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
*/
|
*/
|
||||||
public function untransform(array $item): array
|
public function untransform(array $item): array
|
||||||
{
|
{
|
||||||
$rewatching = (array_key_exists('reconsuming', $item['data']) && $item['data']['reconsuming']);
|
|
||||||
|
|
||||||
$map = [
|
$map = [
|
||||||
'id' => $item['mal_id'],
|
'id' => $item['mal_id'],
|
||||||
'data' => [
|
'data' => [
|
||||||
'episode' => $item['data']['progress'],
|
'episode' => $item['data']['progress']
|
||||||
// 'enable_rewatching' => $rewatching,
|
|
||||||
// 'times_rewatched' => $item['data']['reconsumeCount'],
|
|
||||||
// 'comments' => $item['data']['notes'],
|
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if (array_key_exists('rating', $item['data']))
|
switch(TRUE)
|
||||||
{
|
{
|
||||||
$map['data']['score'] = $item['data']['rating'] * 2;
|
case array_key_exists('notes', $item['data']):
|
||||||
}
|
$map['data']['comments'] = $item['data']['notes'];
|
||||||
|
|
||||||
if (array_key_exists('status', $item['data']))
|
case array_key_exists('rating', $item['data']):
|
||||||
{
|
$map['data']['score'] = $item['data']['rating'] * 2;
|
||||||
$map['data']['status'] = self::statusMap[$item['data']['status']];
|
|
||||||
|
case array_key_exists('reconsuming', $item['data']):
|
||||||
|
$map['data']['enable_rewatching'] = (bool) $item['data']['reconsuming'];
|
||||||
|
|
||||||
|
case array_key_exists('reconsumeCount', $item['data']):
|
||||||
|
$map['data']['times_rewatched'] = $item['data']['reconsumeCount'];
|
||||||
|
|
||||||
|
case array_key_exists('status', $item['data']):
|
||||||
|
$map['data']['status'] = self::statusMap[$item['data']['status']];
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return $map;
|
||||||
|
136
tests/API/APIRequestBuilderTest.php
Normal file
136
tests/API/APIRequestBuilderTest.php
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package AnimeListClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2017 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Tests\API;
|
||||||
|
|
||||||
|
use Amp;
|
||||||
|
use Amp\Artax\Client;
|
||||||
|
use Aviat\AnimeClient\API\APIRequestBuilder;
|
||||||
|
use Aviat\Ion\Json;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Psr\Log\NullLogger;
|
||||||
|
|
||||||
|
class APIRequestBuilderTest extends TestCase {
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->builder = new class extends APIRequestBuilder {
|
||||||
|
protected $baseUrl = 'https://httpbin.org/';
|
||||||
|
|
||||||
|
protected $defaultHeaders = ['User-Agent' => "Tim's Anime Client Testsuite / 4.0"];
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->builder->setLogger(new NullLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGzipRequest()
|
||||||
|
{
|
||||||
|
$request = $this->builder->newRequest('GET', 'gzip')
|
||||||
|
->getFullRequest();
|
||||||
|
$response = Amp\wait((new Client)->request($request));
|
||||||
|
$body = Json::decode($response->getBody());
|
||||||
|
$this->assertEquals(1, $body['gzipped']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidRequestMethod()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->builder->newRequest('FOO', 'gzip')
|
||||||
|
->getFullRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRequestWithBasicAuth()
|
||||||
|
{
|
||||||
|
$request = $this->builder->newRequest('GET', 'headers')
|
||||||
|
->setBasicAuth('username', 'password')
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
$response = Amp\wait((new Client)->request($request));
|
||||||
|
$body = Json::decode($response->getBody());
|
||||||
|
|
||||||
|
$this->assertEquals('Basic dXNlcm5hbWU6cGFzc3dvcmQ=', $body['headers']['Authorization']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRequestWithQueryString()
|
||||||
|
{
|
||||||
|
$query = [
|
||||||
|
'foo' => 'bar',
|
||||||
|
'bar' => [
|
||||||
|
'foo' => 'bar'
|
||||||
|
],
|
||||||
|
'baz' => [
|
||||||
|
'bar' => 'foo'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'foo' => 'bar',
|
||||||
|
'bar[foo]' => 'bar',
|
||||||
|
'baz[bar]' => 'foo'
|
||||||
|
];
|
||||||
|
|
||||||
|
$request = $this->builder->newRequest('GET', 'get')
|
||||||
|
->setQuery($query)
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
$response = Amp\wait((new Client)->request($request));
|
||||||
|
$body = Json::decode($response->getBody());
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $body['args']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormValueRequest()
|
||||||
|
{
|
||||||
|
$formValues = [
|
||||||
|
'foo' => 'bar',
|
||||||
|
'bar' => 'foo'
|
||||||
|
];
|
||||||
|
|
||||||
|
$request = $this->builder->newRequest('POST', 'post')
|
||||||
|
->setFormFields($formValues)
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
$response = Amp\wait((new Client)->request($request));
|
||||||
|
$body = Json::decode($response->getBody());
|
||||||
|
|
||||||
|
$this->assertEquals($formValues, $body['form']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFullUrlRequest()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'foo' => [
|
||||||
|
'bar' => 1,
|
||||||
|
'baz' => [2, 3, 4],
|
||||||
|
'bar' => [
|
||||||
|
'a' => 1,
|
||||||
|
'b' => 2
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$request = $this->builder->newRequest('PUT', 'https://httpbin.org/put')
|
||||||
|
->setHeader('Content-Type', 'application/json')
|
||||||
|
->setBody(Json::encode($data))
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
$response = Amp\wait((new Client)->request($request));
|
||||||
|
$body = Json::decode($response->getBody());
|
||||||
|
|
||||||
|
$this->assertEquals($data, $body['json']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user