Making API requests to Anilist, see #5

This commit is contained in:
Timothy Warren 2018-08-15 14:05:28 -04:00
parent b5f8413ceb
commit b3a3e19146
8 changed files with 283 additions and 165 deletions

View File

@ -41,11 +41,27 @@ final class Anilist {
];
const ANILIST_KITSU_WATCHING_STATUS_MAP = [
'CURRENT' => KAWS::WATCHING,
'COMPLETED' => KAWS::COMPLETED,
'PAUSED' => KAWS::ON_HOLD,
'DROPPED' => KAWS::DROPPED,
'PLANNING' => KAWS::PLAN_TO_WATCH,
AnimeWatchingStatus::WATCHING => KAWS::WATCHING,
AnimeWatchingStatus::COMPLETED => KAWS::COMPLETED,
AnimeWatchingStatus::ON_HOLD => KAWS::ON_HOLD,
AnimeWatchingStatus::DROPPED => KAWS::DROPPED,
AnimeWatchingStatus::PLAN_TO_WATCH => KAWS::PLAN_TO_WATCH,
];
const KITSU_ANILIST_READING_STATUS_MAP = [
KMRS::READING => MangaReadingStatus::READING,
KMRS::COMPLETED => MangaReadingStatus::COMPLETED,
KMRS::ON_HOLD => MangaReadingStatus::ON_HOLD,
KMRS::DROPPED => MangaReadingStatus::DROPPED,
KMRS::PLAN_TO_READ => MangaReadingStatus::PLAN_TO_READ,
];
const ANILIST_KITSU_READING_STATUS_MAP = [
MangaReadingStatus::READING => KMRS::READING,
MangaReadingStatus::COMPLETED => KMRS::COMPLETED,
MangaReadingStatus::ON_HOLD => KMRS::ON_HOLD,
MangaReadingStatus::DROPPED => KMRS::DROPPED,
MangaReadingStatus::PLAN_TO_READ => KMRS::PLAN_TO_READ,
];
public static function getIdToWatchingStatusMap()
@ -67,7 +83,8 @@ final class Anilist {
'COMPLETED' => MangaReadingStatus::COMPLETED,
'PAUSED' => MangaReadingStatus::ON_HOLD,
'DROPPED' => MangaReadingStatus::DROPPED,
'PLANNING' => MangaReadingStatus::PLAN_TO_READ
'PLANNING' => MangaReadingStatus::PLAN_TO_READ,
'REPEATING' => MangaReadingStatus::READING,
];
}
}

View File

@ -16,14 +16,20 @@
namespace Aviat\AnimeClient\API\Anilist;
use Amp\Artax\Request;
use Amp\Artax\Response;
use function Amp\Promise\wait;
use Aviat\AnimeClient\API\{
Anilist,
HummingbirdClient
};
use const Aviat\AnimeClient\SESSION_SEGMENT;
use Aviat\Ion\Json;
use Aviat\Ion\Di\ContainerAware;
trait AnilistTrait {
use ContainerAware;
/**
* The request builder for the MAL API
@ -66,27 +72,92 @@ trait AnilistTrait {
* @param string $url
* @param array $options
* @return \Amp\Artax\Response
* @return Request
*/
public function setUpRequest(string $url, array $options = [])
public function setUpRequest(string $url, array $options = []): Request
{
// @TODO Implement
$config = $this->getContainer()->get('config');
$anilistConfig = $config->get('anilist');
$request = $this->requestBuilder->newRequest('POST', $url);
$sessionSegment = $this->getContainer()
->get('session')
->getSegment(SESSION_SEGMENT);
$authenticated = $sessionSegment->get('auth_token') !== NULL;
if ($authenticated)
{
$request = $request->setAuth('bearer', $anilistConfig['access_token']);
}
if (array_key_exists('form_params', $options)) {
$request = $request->setFormFields($options['form_params']);
}
if (array_key_exists('query', $options)) {
$request = $request->setQuery($options['query']);
}
if (array_key_exists('body', $options)) {
$request = $request->setJsonBody($options['body']);
}
if (array_key_exists('headers', $options)) {
$request = $request->setHeaders($options['headers']);
}
return $request->getFullRequest();
}
/**
* Run a GraphQL API query
*
* @param string $name
* @param array $variables
* @return array
*/
public function runQuery(string $name, array $variables = []): array
{
$file = realpath(__DIR__ . "/GraphQL/Queries/{$name}.graphql");
if ( ! file_exists($file))
{
throw new \LogicException('GraphQL query file does not exist.');
}
// $query = str_replace(["\t", "\n"], ' ', file_get_contents($file));
$query = file_get_contents($file);
$body = [
'query' => $query
];
if ( ! empty($variables))
{
$body['variables'] = [];
foreach($variables as $key => $val)
{
$body['variables'][$key] = $val;
}
}
return $this->postRequest([
'body' => $body
]);
}
/**
* Make a request
*
* @param string $type
* @param string $url
* @param array $options
* @return \Amp\Artax\Response
* @return Response
*/
private function getResponse(string $type, string $url, array $options = [])
private function getResponse(string $url, array $options = []): Response
{
$logger = NULL;
if ($this->getContainer())
{
$logger = $this->container->getLogger('mal-request');
$logger = $this->container->getLogger('anilist-request');
}
$request = $this->setUpRequest($url, $options);
@ -104,14 +175,12 @@ trait AnilistTrait {
}
/**
* Make a request
* Remove some boilerplate for post requests
*
* @param string $type
* @param string $url
* @param array $options
* @return array
*/
private function request(string $type, string $url, array $options = []): array
protected function postRequest(array $options = []): array
{
$logger = NULL;
if ($this->getContainer())
@ -119,44 +188,19 @@ trait AnilistTrait {
$logger = $this->container->getLogger('anilist-request');
}
$response = $this->getResponse($type, $url, $options);
if ((int) $response->getStatus() > 299 OR (int) $response->getStatus() < 200)
{
if ($logger)
{
$logger->warning('Non 200 response for api call', (array)$response->getBody());
}
}
return XML::toArray(wait($response->getBody()));
}
/**
* Remove some boilerplate for post requests
*
* @param mixed ...$args
* @return array
*/
protected function postRequest(...$args): array
{
$logger = NULL;
if ($this->getContainer())
{
$logger = $this->container->getLogger('anilist-request');
}
$response = $this->getResponse('POST', ...$args);
$response = $this->getResponse(Anilist::BASE_URL, $options);
$validResponseCodes = [200, 201];
if ( ! \in_array((int) $response->getStatus(), $validResponseCodes, TRUE))
if ( ! \in_array($response->getStatus(), $validResponseCodes, TRUE))
{
if ($logger)
{
$logger->warning('Non 201 response for POST api call', (array)$response->getBody());
$logger->warning('Non 200 response for POST api call', (array)$response->getBody());
}
}
return XML::toArray($response->getBody());
// dump(wait($response->getBody()));
return Json::decode(wait($response->getBody()));
}
}

View File

@ -0,0 +1,56 @@
query ($name: String) {
MediaListCollection(userName: $name, type: ANIME) {
lists {
entries {
id
mediaId
score
progress
repeat
private
notes
status
media {
id
idMal
title {
romaji
english
native
userPreferred
}
type
format
status
episodes
season
genres
synonyms
countryOfOrigin
source
trailer {
id
}
coverImage {
large
medium
}
bannerImage
tags {
id
}
externalLinks {
id
}
mediaListEntry {
id
}
}
user {
id
}
}
}
}
}

View File

@ -0,0 +1,56 @@
{query ($name: String) {
MediaListCollection(userName: $name, type: MANGA) {
lists {
entries {
id
mediaId
score
progress
progressVolumes
repeat
private
notes
status
media {
id
idMal
title {
romaji
english
native
userPreferred
}
type
format
status
chapters
volumes
genres
synonyms
countryOfOrigin
source
trailer {
id
}
coverImage {
large
medium
}
bannerImage
tags {
id
}
externalLinks {
id
}
mediaListEntry {
id
}
}
user {
id
}
}
}
}
}

View File

@ -39,127 +39,17 @@ final class Model
public function __construct(ListItem $listItem)
{
$this->listItem = $listItem;
}
public function getAnimeList()
public function getAnimeList(): array
{
$graphQL = <<<GQL
{
MediaListCollection(userId: 103470, type: ANIME) {
lists {
entries {
id
mediaId
score
progress
status
media {
id
idMal
title {
romaji
english
native
userPreferred
}
type
format
status
episodes
season
genres
synonyms
countryOfOrigin
source
trailer {
id
}
coverImage {
large
medium
}
bannerImage
tags {
id
}
externalLinks {
id
}
mediaListEntry {
id
}
}
user {
id
}
}
}
}
}
GQL;
return $this->runQuery('UserAnimeList', ['name' => 'timw4mail']);
}
public function getMangaList()
public function getMangaList(): array
{
$graphQL = <<<GQL
{
MediaListCollection(userId: 103470, type: MANGA) {
lists {
entries {
id
mediaId
score
progress
progressVolumes
repeat
private
notes
status
media {
id
idMal
title {
romaji
english
native
userPreferred
}
type
format
status
chapters
volumes
genres
synonyms
countryOfOrigin
source
trailer {
id
}
coverImage {
large
medium
}
bannerImage
tags {
id
}
externalLinks {
id
}
mediaListEntry {
id
}
}
user {
id
}
}
}
}
}
GQL;
return $this->runQuery('UserMangaList', ['name' => 'timw4mail']);
}
// -------------------------------------------------------------------------

View File

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
*
* PHP version 7
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2018 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient\API\Anilist\Transformer;
use Aviat\Ion\Transformer\AbstractTransformer;
class AnimeListTransformer extends AbstractTransformer {
public function transform($item)
{
}
}

View File

@ -0,0 +1,28 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
*
* PHP version 7
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2018 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient\API\Anilist\Transformer;
use Aviat\Ion\Transformer\AbstractTransformer;
class MangaListTransformer extends AbstractTransformer
{
public function transform($item)
{
}
}

View File

@ -22,10 +22,10 @@ use Aviat\Ion\Enum;
* Possible values for watching status for the current anime
*/
final class Anilist extends Enum {
const WATCHING = 'CURRENT';
const READING = 'CURRENT';
const COMPLETED = 'COMPLETED';
const ON_HOLD = 'PAUSED';
const DROPPED = 'DROPPED';
const PLAN_TO_WATCH = 'PLANNING';
const PLAN_TO_READ = 'PLANNING';
const REPEATING = 'REPEATING';
}