Version 5.1 - All the GraphQL #32

Closed
timw4mail wants to merge 1160 commits from develop into master
16 changed files with 328 additions and 118 deletions
Showing only changes of commit a48c3e5f8e - Show all commits

View File

@ -48,10 +48,13 @@ return function(array $config_array = []) {
$app_logger = new Logger('animeclient'); $app_logger = new Logger('animeclient');
$app_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE)); $app_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
$request_logger = new Logger('request'); $kitsu_request_logger = new Logger('kitsu_request');
$request_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/request.log', Logger::NOTICE)); $kitsu_request_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
$mal_request_logger = new Logger('mal_request');
$mal_request_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/mal_request.log', Logger::NOTICE));
$container->setLogger($app_logger, 'default'); $container->setLogger($app_logger, 'default');
$container->setLogger($request_logger, 'request'); $container->setLogger($kitsu_request_logger, 'kitsu_request');
$container->setLogger($mal_request_logger, 'mal_request');
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Injected Objects // Injected Objects

View File

@ -1,5 +1,4 @@
<?php if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<?php /* <pre><?= json_encode($item, \JSON_PRETTY_PRINT); ?></pre> */ ?>
<main> <main>
<h2>Edit Anime List Item</h2> <h2>Edit Anime List Item</h2>
<form action="<?= $action ?>" method="post"> <form action="<?= $action ?>" method="post">
@ -86,15 +85,20 @@
</tbody> </tbody>
</table> </table>
</form> </form>
<br />
<br />
<fieldset> <fieldset>
<legend>Danger Zone</legend> <legend>Danger Zone</legend>
<form class="js-delete" action="<?= $url->generate('anime.delete') ?>" method="post"> <form class="js-delete" action="<?= $url->generate('anime.delete') ?>" method="post">
<table class="form invisible"> <table class="form invisible">
<tbody> <tbody>
<tr> <tr>
<td>&nbsp;</td> <td>
<strong>Permanently</strong> remove this list item and <strong>all</strong> its data?
</td>
<td> <td>
<input type="hidden" value="<?= $item['id'] ?>" name="id" /> <input type="hidden" value="<?= $item['id'] ?>" name="id" />
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id" />
<button type="submit" class="danger">Delete Entry</button> <button type="submit" class="danger">Delete Entry</button>
</td> </td>
</tr> </tr>

View File

@ -28,4 +28,3 @@ class AnimeWatchingStatus extends BaseEnum {
const ON_HOLD = 'on_hold'; const ON_HOLD = 'on_hold';
const DROPPED = 'dropped'; const DROPPED = 'dropped';
} }
// End of AnimeWatchingStatus.php

View File

@ -93,7 +93,7 @@ trait KitsuTrait {
'headers' => $this->defaultHeaders 'headers' => $this->defaultHeaders
]; ];
$logger = $this->container->getLogger('request'); $logger = $this->container->getLogger('kitsu_request');
$sessionSegment = $this->getContainer() $sessionSegment = $this->getContainer()
->get('session') ->get('session')
->getSegment(AnimeClient::SESSION_SEGMENT); ->getSegment(AnimeClient::SESSION_SEGMENT);
@ -106,10 +106,19 @@ trait KitsuTrait {
$options = array_merge($defaultOptions, $options); $options = array_merge($defaultOptions, $options);
$logger->debug(Json::encode([$type, $url])); $response = $this->client->request($type, $url, $options);
$logger->debug(Json::encode($options));
return $this->client->request($type, $url, $options); $logger->debug('Kitsu API request', [
'requestParams' => [
'type' => $type,
'url' => $url,
],
'responseValues' => [
'status' => $response->getStatusCode()
]
]);
return $response;
} }
/** /**
@ -125,7 +134,7 @@ trait KitsuTrait {
$logger = null; $logger = null;
if ($this->getContainer()) if ($this->getContainer())
{ {
$logger = $this->container->getLogger('request'); $logger = $this->container->getLogger('kitsu_request');
} }
$response = $this->getResponse($type, $url, $options); $response = $this->getResponse($type, $url, $options);
@ -134,11 +143,8 @@ trait KitsuTrait {
{ {
if ($logger) if ($logger)
{ {
$logger->warning('Non 200 response for api call'); $logger->warning('Non 200 response for api call', $response->getBody());
$logger->warning($response->getBody());
} }
// throw new RuntimeException($response->getBody());
} }
return JSON::decode($response->getBody(), TRUE); return JSON::decode($response->getBody(), TRUE);
@ -177,7 +183,7 @@ trait KitsuTrait {
$logger = null; $logger = null;
if ($this->getContainer()) if ($this->getContainer())
{ {
$logger = $this->container->getLogger('request'); $logger = $this->container->getLogger('kitsu_request');
} }
$response = $this->getResponse('POST', ...$args); $response = $this->getResponse('POST', ...$args);
@ -187,11 +193,8 @@ trait KitsuTrait {
{ {
if ($logger) if ($logger)
{ {
$logger->warning('Non 201 response for POST api call'); $logger->warning('Non 201 response for POST api call', $response->getBody());
$logger->warning($response->getBody());
} }
// throw new RuntimeException($response->getBody());
} }
return JSON::decode($response->getBody(), TRUE); return JSON::decode($response->getBody(), TRUE);

View File

@ -167,6 +167,34 @@ class Model {
return $this->animeTransformer->transform($baseData); return $this->animeTransformer->transform($baseData);
} }
/**
* Get the mal id for the anime represented by the kitsu id
* to enable updating MyAnimeList
*
* @param string $kitsuAnimeId The id of the anime on Kitsu
* @return string|null Returns the mal id if it exists, otherwise null
*/
public function getMalIdForAnime(string $kitsuAnimeId)
{
$options = [
'query' => [
'include' => 'mappings'
]
];
$data = $this->getRequest("anime/{$kitsuAnimeId}", $options);
$mappings = array_column($data['included'], 'attributes');
foreach($mappings as $map)
{
if ($map['externalSite'] === 'myanimelist/anime')
{
return $map['externalId'];
}
}
return null;
}
/** /**
* Get information about a particular manga * Get information about a particular manga
* *
@ -179,6 +207,16 @@ class Model {
return $this->mangaTransformer->transform($baseData); return $this->mangaTransformer->transform($baseData);
} }
/**
* Get and transform the entirety of the user's anime list
*
* @return array
*/
public function getFullAnimeList(): array
{
}
/** /**
* Get the raw (unorganized) anime list for the configured user * Get the raw (unorganized) anime list for the configured user
* *

View File

@ -113,6 +113,7 @@ class AnimeListTransformer extends AbstractTransformer {
$untransformed = [ $untransformed = [
'id' => $item['id'], 'id' => $item['id'],
'mal_id' => $item['mal_id'] ?? null,
'data' => [ 'data' => [
'status' => $item['watching_status'], 'status' => $item['watching_status'],
'rating' => $item['user_rating'] / 2, 'rating' => $item['user_rating'] / 2,

View File

@ -16,6 +16,10 @@
namespace Aviat\AnimeClient\API; namespace Aviat\AnimeClient\API;
use Aviat\AnimeClient\API\Kitsu\Enum\{
AnimeWatchingStatus as KAWS,
MangaReadingStatus as KMRS
};
use Aviat\AnimeClient\API\MAL\Enum\{AnimeWatchingStatus, MangaReadingStatus}; use Aviat\AnimeClient\API\MAL\Enum\{AnimeWatchingStatus, MangaReadingStatus};
/** /**
@ -25,6 +29,14 @@ class MAL {
const AUTH_URL = 'https://myanimelist.net/api/account/verify_credentials.xml'; const AUTH_URL = 'https://myanimelist.net/api/account/verify_credentials.xml';
const BASE_URL = 'https://myanimelist.net/api/'; const BASE_URL = 'https://myanimelist.net/api/';
const KITSU_MAL_WATCHING_STATUS_MAP = [
KAWS::WATCHING => AnimeWatchingStatus::WATCHING,
KAWS::COMPLETED => AnimeWatchingStatus::COMPLETED,
KAWS::ON_HOLD => AnimeWatchingStatus::ON_HOLD,
KAWS::DROPPED => AnimeWatchingStatus::DROPPED,
KAWS::PLAN_TO_WATCH => AnimeWatchingStatus::PLAN_TO_WATCH
];
public static function getIdToWatchingStatusMap() public static function getIdToWatchingStatusMap()
{ {
return [ return [
@ -32,7 +44,12 @@ class MAL {
2 => AnimeWatchingStatus::COMPLETED, 2 => AnimeWatchingStatus::COMPLETED,
3 => AnimeWatchingStatus::ON_HOLD, 3 => AnimeWatchingStatus::ON_HOLD,
4 => AnimeWatchingStatus::DROPPED, 4 => AnimeWatchingStatus::DROPPED,
5 => AnimeWatchingStatus::PLAN_TO_WATCH 6 => AnimeWatchingStatus::PLAN_TO_WATCH,
'watching' => AnimeWatchingStatus::WATCHING,
'completed' => AnimeWatchingStatus::COMPLETED,
'onhold' => AnimeWatchingStatus::ON_HOLD,
'dropped' => AnimeWatchingStatus::DROPPED,
'plantowatch' => AnimeWatchingStatus::PLAN_TO_WATCH
]; ];
} }
@ -43,7 +60,12 @@ class MAL {
2 => MangaReadingStatus::COMPLETED, 2 => MangaReadingStatus::COMPLETED,
3 => MangaReadingStatus::ON_HOLD, 3 => MangaReadingStatus::ON_HOLD,
4 => MangaReadingStatus::DROPPED, 4 => MangaReadingStatus::DROPPED,
5 => MangaReadingStatus::PLAN_TO_READ 6 => MangaReadingStatus::PLAN_TO_READ,
'reading' => MangaReadingStatus::READING,
'completed' => MangaReadingStatus::COMPLETED,
'onhold' => MangaReadingStatus::ON_HOLD,
'dropped' => MangaReadingStatus::DROPPED,
'plantoread' => MangaReadingStatus::PLAN_TO_WATCH
]; ];
} }
} }

View File

@ -22,9 +22,9 @@ use Aviat\Ion\Enum as BaseEnum;
* Possible values for watching status for the current anime * Possible values for watching status for the current anime
*/ */
class AnimeWatchingStatus extends BaseEnum { class AnimeWatchingStatus extends BaseEnum {
const WATCHING = 'watching'; const WATCHING = 1;
const COMPLETED = 'completed'; const COMPLETED = 2;
const ON_HOLD = 'onhold'; const ON_HOLD = 3;
const DROPPED = 'dropped'; const DROPPED = 4;
const PLAN_TO_WATCH = 'plantowatch'; const PLAN_TO_WATCH = 6;
} }

View File

@ -33,24 +33,24 @@ class ListItem {
public function create(array $data): bool public function create(array $data): bool
{ {
$id = $data['id']; $id = $data['id'];
$body = (new FormBody) $createData = [
->addField('id', $data['id']) 'id' => $id,
->addField('data', XML::toXML(['entry' => $data['data']])); 'data' => XML::toXML([
'entry' => $data['data']
])
];
$response = $this->getResponse('POST', "animelist/add/{$id}.xml", [ $response = $this->getResponse('POST', "animelist/add/{$id}.xml", [
'headers' => [ 'body' => $this->fixBody((new FormBody)->addFields($createData))
'Content-type' => 'application/x-www-form-urlencoded',
'Accept' => 'text/plain'
],
'body' => $body
]); ]);
return $response->getStatus() === 201; return $response->getBody() === 'Created';
} }
public function delete(string $id): bool public function delete(string $id): bool
{ {
$response = $this->getResponse('DELETE', "animeclient/delete/{$id}.xml", [ $response = $this->getResponse('DELETE', "animelist/delete/{$id}.xml", [
'body' => (new FormBody)->addField('id', $id) 'body' => $this->fixBody((new FormBody)->addField('id', $id))
]); ]);
return $response->getBody() === 'Deleted'; return $response->getBody() === 'Deleted';
@ -61,18 +61,15 @@ class ListItem {
return []; return [];
} }
public function update(string $id, array $data): Response public function update(string $id, array $data)
{ {
$xml = XML::toXML(['entry' => $data]);
$body = (new FormBody) $body = (new FormBody)
->addField('id', $id) ->addField('id', $id)
->addField('data', XML::toXML(['entry' => $data])) ->addField('data', $xml);
return $this->postRequest("animelist/update/{$id}.xml", [ return $this->getResponse('POST', "animelist/update/{$id}.xml", [
'headers' => [ 'body' => $this->fixBody($body)
'Content-type' => 'application/x-www-form-urlencoded',
'Accept' => 'text/plain'
],
'body' => $body
]); ]);
} }
} }

View File

@ -16,7 +16,7 @@
namespace Aviat\AnimeClient\API\MAL; namespace Aviat\AnimeClient\API\MAL;
use Amp\Artax\{Client, Request}; use Amp\Artax\{Client, FormBody, Request};
use Aviat\AnimeClient\API\{ use Aviat\AnimeClient\API\{
MAL as M, MAL as M,
XML XML
@ -38,9 +38,27 @@ trait MALTrait {
* @var array * @var array
*/ */
protected $defaultHeaders = [ protected $defaultHeaders = [
'Accept' => 'text/xml',
'Accept-Encoding' => 'gzip',
'Content-type' => 'application/x-www-form-urlencoded',
'User-Agent' => "Tim's Anime Client/4.0" 'User-Agent' => "Tim's Anime Client/4.0"
]; ];
/**
* Unencode the dual-encoded ampersands in the body
*
* This is a dirty hack until I can fully track down where
* the dual-encoding happens
*
* @param FormBody $formBody The form builder object to fix
* @return string
*/
private function fixBody(FormBody $formBody): string
{
$rawBody = \Amp\wait($formBody->getBody());
return html_entity_decode($rawBody, \ENT_HTML5, 'UTF-8');
}
/** /**
* Make a request via Guzzle * Make a request via Guzzle
* *
@ -51,8 +69,10 @@ trait MALTrait {
*/ */
private function getResponse(string $type, string $url, array $options = []) private function getResponse(string $type, string $url, array $options = [])
{ {
$this->defaultHeaders['User-Agent'] = $_SERVER['HTTP_USER_AGENT'] ?? $this->defaultHeaders;
$type = strtoupper($type); $type = strtoupper($type);
$validTypes = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']; $validTypes = ['GET', 'POST', 'DELETE'];
if ( ! in_array($type, $validTypes)) if ( ! in_array($type, $validTypes))
{ {
@ -60,7 +80,7 @@ trait MALTrait {
} }
$config = $this->container->get('config'); $config = $this->container->get('config');
$logger = $this->container->getLogger('request'); $logger = $this->container->getLogger('mal_request');
$headers = array_merge($this->defaultHeaders, $options['headers'] ?? [], [ $headers = array_merge($this->defaultHeaders, $options['headers'] ?? [], [
'Authorization' => 'Basic ' . 'Authorization' => 'Basic ' .
@ -70,20 +90,39 @@ trait MALTrait {
$query = $options['query'] ?? []; $query = $options['query'] ?? [];
$url = (strpos($url, '//') !== FALSE) $url = (strpos($url, '//') !== FALSE)
? $url . '?' . http_build_query($query) ? $url
: $this->baseUrl . $url . '?' . http_build_query($query); : $this->baseUrl . $url;
if ( ! empty($query))
{
$url .= '?' . http_build_query($query);
}
$request = (new Request) $request = (new Request)
->setMethod($type) ->setMethod($type)
->setUri($url) ->setUri($url)
->setProtocol('1.1') ->setProtocol('1.1')
->setAllHeaders($headers) ->setAllHeaders($headers);
->setBody($options['body']);
$logger->debug(Json::encode([$type, $url])); if (array_key_exists('body', $options))
$logger->debug(Json::encode($options)); {
$request->setBody($options['body']);
}
return \Amp\wait((new Client)->request($request)); $response = \Amp\wait((new Client)->request($request));
$logger->debug('MAL api request', [
'url' => $url,
'status' => $response->getStatus(),
'reason' => $response->getReason(),
'headers' => $response->getAllHeaders(),
'requestHeaders' => $request->getAllHeaders(),
'requestBody' => $request->hasBody() ? $request->getBody() : 'No request body',
'requestBodyBeforeEncode' => $request->hasBody() ? urldecode($request->getBody()) : '',
'body' => $response->getBody()
]);
return $response;
} }
/** /**
@ -99,7 +138,7 @@ trait MALTrait {
$logger = null; $logger = null;
if ($this->getContainer()) if ($this->getContainer())
{ {
$logger = $this->container->getLogger('request'); $logger = $this->container->getLogger('mal_request');
} }
$response = $this->getResponse($type, $url, $options); $response = $this->getResponse($type, $url, $options);
@ -108,8 +147,7 @@ trait MALTrait {
{ {
if ($logger) if ($logger)
{ {
$logger->warning('Non 200 response for api call'); $logger->warning('Non 200 response for api call', $response->getBody());
$logger->warning($response->getBody());
} }
} }
@ -138,7 +176,7 @@ trait MALTrait {
$logger = null; $logger = null;
if ($this->getContainer()) if ($this->getContainer())
{ {
$logger = $this->container->getLogger('request'); $logger = $this->container->getLogger('mal_request');
} }
$response = $this->getResponse('POST', ...$args); $response = $this->getResponse('POST', ...$args);
@ -148,8 +186,7 @@ trait MALTrait {
{ {
if ($logger) if ($logger)
{ {
$logger->warning('Non 201 response for POST api call'); $logger->warning('Non 201 response for POST api call', $response->getBody());
$logger->warning($response->getBody());
} }
} }

View File

@ -17,13 +17,10 @@
namespace Aviat\AnimeClient\API\MAL; namespace Aviat\AnimeClient\API\MAL;
use Aviat\AnimeClient\API\MAL as M; use Aviat\AnimeClient\API\MAL as M;
use Aviat\AnimeClient\API\MAL\{ use Aviat\AnimeClient\API\MAL\ListItem;
AnimeListTransformer, use Aviat\AnimeClient\API\MAL\Transformer\AnimeListTransformer;
ListItem
};
use Aviat\AnimeClient\API\XML; use Aviat\AnimeClient\API\XML;
use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Di\ContainerAware;
use Aviat\Ion\Json;
/** /**
* MyAnimeList API Model * MyAnimeList API Model
@ -42,13 +39,37 @@ class Model {
*/ */
public function __construct(ListItem $listItem) public function __construct(ListItem $listItem)
{ {
//$this->animeListTransformer = new AnimeListTransformer(); $this->animeListTransformer = new AnimeListTransformer();
$this->listItem = $listItem; $this->listItem = $listItem;
} }
public function createListItem(array $data): bool public function createListItem(array $data): bool
{ {
return $this->listItem->create($data); $createData = [
'id' => $data['id'],
'data' => [
'status' => M::KITSU_MAL_WATCHING_STATUS_MAP[$data['status']]
]
];
return $this->listItem->create($createData);
}
public function getFullList(): array
{
$config = $this->container->get('config');
$userName = $config->get(['mal', 'username']);
$list = $this->getRequest('https://myanimelist.net/malappinfo.php', [
'headers' => [
'Accept' => 'text/xml'
],
'query' => [
'u' => $userName,
'status' => 'all'
]
]);
return $list;//['anime'];
} }
public function getListItem(string $listId): array public function getListItem(string $listId): array
@ -58,8 +79,8 @@ class Model {
public function updateListItem(array $data) public function updateListItem(array $data)
{ {
//$updateData = $this->animeListTransformer->transform($data['data']); $updateData = $this->animeListTransformer->untransform($data);
return $this->listItem->update($data['mal_id'], $updateData); return $this->listItem->update($updateData['id'], $updateData['data']);
} }
public function deleteListItem(string $id): bool public function deleteListItem(string $id): bool

View File

@ -14,8 +14,9 @@
* @link https://github.com/timw4mail/HummingBirdAnimeClient * @link https://github.com/timw4mail/HummingBirdAnimeClient
*/ */
namespace Aviat\AnimeClient\API\MAL; namespace Aviat\AnimeClient\API\MAL\Transformer;
use Aviat\AnimeClient\API\Kitsu\Enum\AnimeWatchingStatus;
use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\Transformer\AbstractTransformer;
/** /**
@ -23,18 +24,22 @@ use Aviat\Ion\Transformer\AbstractTransformer;
*/ */
class AnimeListTransformer extends AbstractTransformer { class AnimeListTransformer extends AbstractTransformer {
const statusMap = [
AnimeWatchingStatus::WATCHING => '1',
AnimeWatchingStatus::COMPLETED => '2',
AnimeWatchingStatus::ON_HOLD => '3',
AnimeWatchingStatus::DROPPED => '4',
AnimeWatchingStatus::PLAN_TO_WATCH => '6'
];
public function transform($item) public function transform($item)
{ {
$rewatching = 'false'; $rewatching = (array_key_exists('rewatching', $item) && $item['rewatching']);
if (array_key_exists('rewatching', $item) && $item['rewatching'])
{
$rewatching = 'true';
}
return [ return [
'id' => $item['id'], 'id' => $item['mal_id'],
'data' => [ 'data' => [
'status' => $item['watching_status'], 'status' => self::statusMap[$item['watching_status']],
'rating' => $item['user_rating'], 'rating' => $item['user_rating'],
'rewatch_value' => (int) $rewatching, 'rewatch_value' => (int) $rewatching,
'times_rewatched' => $item['rewatched'], 'times_rewatched' => $item['rewatched'],
@ -43,4 +48,31 @@ class AnimeListTransformer extends AbstractTransformer {
] ]
]; ];
} }
/**
* Transform Kitsu episode data to MAL episode data
*
* @param array $item
* @return array
*/
public function untransform(array $item): array
{
$rewatching = (array_key_exists('reconsuming', $item['data']) && $item['data']['reconsuming']);
$map = [
'id' => $item['mal_id'],
'data' => [
'episode' => $item['data']['progress'],
'status' => self::statusMap[$item['data']['status']],
'score' => (array_key_exists('rating', $item['data']))
? $item['data']['rating'] * 2
: "",
// 'enable_rewatching' => $rewatching,
// 'times_rewatched' => $item['data']['reconsumeCount'],
// 'comments' => $item['data']['notes'],
]
];
return $map;
}
} }

View File

@ -107,12 +107,7 @@ class XML {
{ {
$data = []; $data = [];
// Get rid of unimportant text nodes by removing $xml = static::stripXMLWhitespace($xml);
// whitespace characters from between xml tags,
// except for the xml declaration tag, Which looks
// something like:
/* <?xml version="1.0" encoding="UTF-8"?> */
$xml = preg_replace('/([^\?])>\s+</', '$1><', $xml);
$dom = new DOMDocument(); $dom = new DOMDocument();
$dom->loadXML($xml); $dom->loadXML($xml);
@ -166,6 +161,16 @@ class XML {
return static::toXML($this->getData()); return static::toXML($this->getData());
} }
private static function stripXMLWhitespace(string $xml): string
{
// Get rid of unimportant text nodes by removing
// whitespace characters from between xml tags,
// except for the xml declaration tag, Which looks
// something like:
/* <?xml version="1.0" encoding="UTF-8"?> */
return preg_replace('/([^\?])>\s+</', '$1><', $xml);
}
/** /**
* Recursively create array structure based on xml structure * Recursively create array structure based on xml structure
* *

View File

@ -259,7 +259,7 @@ class Anime extends BaseController {
$data = $this->request->getParsedBody(); $data = $this->request->getParsedBody();
} }
$response = $this->model->updateLibraryItem($data); $response = $this->model->updateLibraryItem($data, $data);
$this->cache->clear(); $this->cache->clear();
$this->outputJSON($response['body'], $response['statusCode']); $this->outputJSON($response['body'], $response['statusCode']);
@ -273,7 +273,7 @@ class Anime extends BaseController {
public function delete() public function delete()
{ {
$body = $this->request->getParsedBody(); $body = $this->request->getParsedBody();
$response = $this->model->deleteLibraryItem($body['id']); $response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']);
if ((bool)$response === TRUE) if ((bool)$response === TRUE)
{ {

View File

@ -43,6 +43,12 @@ class Anime extends API {
AnimeWatchingStatus::COMPLETED => self::COMPLETED, AnimeWatchingStatus::COMPLETED => self::COMPLETED,
]; ];
protected $kitsuModel;
protected $malModel;
protected $useMALAPI;
/** /**
* Anime constructor. * Anime constructor.
* @param ContainerInterface $container * @param ContainerInterface $container
@ -50,7 +56,11 @@ class Anime extends API {
public function __construct(ContainerInterface $container) { public function __construct(ContainerInterface $container) {
parent::__construct($container); parent::__construct($container);
$config = $container->get('config');
$this->kitsuModel = $container->get('kitsu-model'); $this->kitsuModel = $container->get('kitsu-model');
$this->malModel = $container->get('mal-model');
$this->useMALAPI = $config->get(['use_mal_api']) === TRUE;
} }
/** /**
@ -110,8 +120,26 @@ class Anime extends API {
return $this->kitsuModel->getListItem($itemId); return $this->kitsuModel->getListItem($itemId);
} }
/**
* Add an anime to your list
*
* @param array $data
* @return bool
*/
public function createLibraryItem(array $data): bool public function createLibraryItem(array $data): bool
{ {
if ($this->useMALAPI)
{
$malData = $data;
$malId = $this->kitsuModel->getMalIdForAnime($malData['id']);
if ( ! is_null($malId))
{
$malData['id'] = $malId;
$this->malModel->createListItem($malData);
}
}
return $this->kitsuModel->createListItem($data); return $this->kitsuModel->createListItem($data);
} }
@ -123,11 +151,28 @@ class Anime extends API {
*/ */
public function updateLibraryItem(array $data): array public function updateLibraryItem(array $data): array
{ {
if ($this->useMALAPI)
{
$this->malModel->updateListItem($data);
}
return $this->kitsuModel->updateListItem($data); return $this->kitsuModel->updateListItem($data);
} }
public function deleteLibraryItem($id): bool /**
* Delete a list entry
*
* @param string $id
* @param string|null $malId
* @return bool
*/
public function deleteLibraryItem(string $id, string $malId = null): bool
{ {
if ($this->useMALAPI && ! is_null($malId))
{
$this->malModel->deleteListItem($malId);
}
return $this->kitsuModel->deleteListItem($id); return $this->kitsuModel->deleteListItem($id);
} }
} }

View File

@ -44,6 +44,7 @@ class AnimeListTransformerTest extends AnimeClient_TestCase {
], ],
'expected' => [ 'expected' => [
'id' => 14047981, 'id' => 14047981,
'mal_id' => null,
'data' => [ 'data' => [
'status' => 'current', 'status' => 'current',
'rating' => 4, 'rating' => 4,
@ -57,6 +58,7 @@ class AnimeListTransformerTest extends AnimeClient_TestCase {
], [ ], [
'input' => [ 'input' => [
'id' => 14047981, 'id' => 14047981,
'mal_id' => '12345',
'watching_status' => 'current', 'watching_status' => 'current',
'user_rating' => 8, 'user_rating' => 8,
'episodes_watched' => 38, 'episodes_watched' => 38,
@ -68,6 +70,7 @@ class AnimeListTransformerTest extends AnimeClient_TestCase {
], ],
'expected' => [ 'expected' => [
'id' => 14047981, 'id' => 14047981,
'mal_id' => '12345',
'data' => [ 'data' => [
'status' => 'current', 'status' => 'current',
'rating' => 4, 'rating' => 4,