Version 5.1 - All the GraphQL #32
@ -32,7 +32,7 @@ test:hhvm:
|
|||||||
- /usr/local/bin/composer self-update
|
- /usr/local/bin/composer self-update
|
||||||
- curl -Lo /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
|
- curl -Lo /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
|
||||||
- chmod +x /usr/local/bin/phpunit
|
- chmod +x /usr/local/bin/phpunit
|
||||||
- composer install --no-dev
|
- composer install --no-dev --ignore-platform-reqs
|
||||||
image: 51systems/docker-gitlab-ci-runner-hhvm
|
image: 51systems/docker-gitlab-ci-runner-hhvm
|
||||||
script:
|
script:
|
||||||
- hhvm -d hhvm.php7.all=true /usr/local/bin/phpunit -c build --coverage-text --colors=never
|
- hhvm -d hhvm.php7.all=true /usr/local/bin/phpunit -c build --coverage-text --colors=never
|
@ -1,10 +1,11 @@
|
|||||||
language: php
|
language: php
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- composer install
|
- composer install --ignore-platform-reqs
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 7
|
- 7
|
||||||
|
- 7.1
|
||||||
- hhvm
|
- hhvm
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\{
|
||||||
|
DEFAULT_CONTROLLER_METHOD,
|
||||||
|
DEFAULT_CONTROLLER_NAMESPACE
|
||||||
|
};
|
||||||
|
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -148,25 +153,25 @@ return [
|
|||||||
'cache_purge' => [
|
'cache_purge' => [
|
||||||
'path' => '/cache_purge',
|
'path' => '/cache_purge',
|
||||||
'action' => 'clearCache',
|
'action' => 'clearCache',
|
||||||
'controller' => AnimeClient::DEFAULT_CONTROLLER_NAMESPACE,
|
'controller' => DEFAULT_CONTROLLER_NAMESPACE,
|
||||||
'verb' => 'get',
|
'verb' => 'get',
|
||||||
],
|
],
|
||||||
'login' => [
|
'login' => [
|
||||||
'path' => '/login',
|
'path' => '/login',
|
||||||
'action' => 'login',
|
'action' => 'login',
|
||||||
'controller' => AnimeClient::DEFAULT_CONTROLLER_NAMESPACE,
|
'controller' => DEFAULT_CONTROLLER_NAMESPACE,
|
||||||
'verb' => 'get',
|
'verb' => 'get',
|
||||||
],
|
],
|
||||||
'login.post' => [
|
'login.post' => [
|
||||||
'path' => '/login',
|
'path' => '/login',
|
||||||
'action' => 'loginAction',
|
'action' => 'loginAction',
|
||||||
'controller' => AnimeClient::DEFAULT_CONTROLLER_NAMESPACE,
|
'controller' => DEFAULT_CONTROLLER_NAMESPACE,
|
||||||
'verb' => 'post',
|
'verb' => 'post',
|
||||||
],
|
],
|
||||||
'logout' => [
|
'logout' => [
|
||||||
'path' => '/logout',
|
'path' => '/logout',
|
||||||
'action' => 'logout',
|
'action' => 'logout',
|
||||||
'controller' => AnimeClient::DEFAULT_CONTROLLER_NAMESPACE,
|
'controller' => DEFAULT_CONTROLLER_NAMESPACE,
|
||||||
],
|
],
|
||||||
'update' => [
|
'update' => [
|
||||||
'path' => '/{controller}/update',
|
'path' => '/{controller}/update',
|
||||||
@ -194,7 +199,7 @@ return [
|
|||||||
],
|
],
|
||||||
'list' => [
|
'list' => [
|
||||||
'path' => '/{controller}/{type}{/view}',
|
'path' => '/{controller}/{type}{/view}',
|
||||||
'action' => AnimeClient::DEFAULT_CONTROLLER_METHOD,
|
'action' => DEFAULT_CONTROLLER_METHOD,
|
||||||
'tokens' => [
|
'tokens' => [
|
||||||
'type' => '[a-z_]+',
|
'type' => '[a-z_]+',
|
||||||
'view' => '[a-z_]+',
|
'view' => '[a-z_]+',
|
||||||
@ -202,7 +207,7 @@ return [
|
|||||||
],
|
],
|
||||||
'index_redirect' => [
|
'index_redirect' => [
|
||||||
'path' => '/',
|
'path' => '/',
|
||||||
'controller' => AnimeClient::DEFAULT_CONTROLLER_NAMESPACE,
|
'controller' => DEFAULT_CONTROLLER_NAMESPACE,
|
||||||
'action' => 'redirectToDefaultRoute',
|
'action' => 'redirectToDefaultRoute',
|
||||||
],
|
],
|
||||||
],
|
],
|
@ -19,17 +19,9 @@ namespace Aviat\AnimeClient;
|
|||||||
use Aura\Html\HelperLocatorFactory;
|
use Aura\Html\HelperLocatorFactory;
|
||||||
use Aura\Router\RouterContainer;
|
use Aura\Router\RouterContainer;
|
||||||
use Aura\Session\SessionFactory;
|
use Aura\Session\SessionFactory;
|
||||||
use Aviat\AnimeClient\API\Kitsu\{
|
use Aviat\AnimeClient\API\{Kitsu, MAL};
|
||||||
Auth as KitsuAuth,
|
use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder;
|
||||||
ListItem as KitsuListItem,
|
use Aviat\AnimeClient\API\MAL\MALRequestBuilder;
|
||||||
KitsuRequestBuilder,
|
|
||||||
Model as KitsuModel
|
|
||||||
};
|
|
||||||
use Aviat\AnimeClient\API\MAL\{
|
|
||||||
ListItem as MALListItem,
|
|
||||||
MALRequestBuilder,
|
|
||||||
Model as MALModel
|
|
||||||
};
|
|
||||||
use Aviat\AnimeClient\Model;
|
use Aviat\AnimeClient\Model;
|
||||||
use Aviat\Banker\Pool;
|
use Aviat\Banker\Pool;
|
||||||
use Aviat\Ion\Config;
|
use Aviat\Ion\Config;
|
||||||
@ -120,11 +112,11 @@ return function(array $config_array = []) {
|
|||||||
$requestBuilder = new KitsuRequestBuilder();
|
$requestBuilder = new KitsuRequestBuilder();
|
||||||
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
|
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
|
||||||
|
|
||||||
$listItem = new KitsuListItem();
|
$listItem = new Kitsu\ListItem();
|
||||||
$listItem->setContainer($container);
|
$listItem->setContainer($container);
|
||||||
$listItem->setRequestBuilder($requestBuilder);
|
$listItem->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
$model = new KitsuModel($listItem);
|
$model = new Kitsu\Model($listItem);
|
||||||
$model->setContainer($container);
|
$model->setContainer($container);
|
||||||
$model->setRequestBuilder($requestBuilder);
|
$model->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
@ -136,11 +128,11 @@ return function(array $config_array = []) {
|
|||||||
$requestBuilder = new MALRequestBuilder();
|
$requestBuilder = new MALRequestBuilder();
|
||||||
$requestBuilder->setLogger($container->getLogger('mal-request'));
|
$requestBuilder->setLogger($container->getLogger('mal-request'));
|
||||||
|
|
||||||
$listItem = new MALListItem();
|
$listItem = new MAL\ListItem();
|
||||||
$listItem->setContainer($container);
|
$listItem->setContainer($container);
|
||||||
$listItem->setRequestBuilder($requestBuilder);
|
$listItem->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
$model = new MALModel($listItem);
|
$model = new MAL\Model($listItem);
|
||||||
$model->setContainer($container);
|
$model->setContainer($container);
|
||||||
$model->setRequestBuilder($requestBuilder);
|
$model->setRequestBuilder($requestBuilder);
|
||||||
return $model;
|
return $model;
|
||||||
@ -161,7 +153,7 @@ return function(array $config_array = []) {
|
|||||||
|
|
||||||
// Miscellaneous Classes
|
// Miscellaneous Classes
|
||||||
$container->set('auth', function($container) {
|
$container->set('auth', function($container) {
|
||||||
return new KitsuAuth($container);
|
return new Kitsu\Auth($container);
|
||||||
});
|
});
|
||||||
$container->set('url-generator', function($container) {
|
$container->set('url-generator', function($container) {
|
||||||
return new UrlGenerator($container);
|
return new UrlGenerator($container);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<meta http-equiv="cache-control" content="no-store" />
|
<meta http-equiv="cache-control" content="no-store" />
|
||||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
|
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<link rel="stylesheet" href="<?= $urlGenerator->asset_url('css.php/g/base') ?>" />
|
<link rel="stylesheet" href="<?= $urlGenerator->asset_url('css.php/g/base') ?>" />
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php/g/base') ?>"></script>
|
<script src="<?= $urlGenerator->asset_url('js.php/g/base') ?>"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
"description": "A self-hosted anime/manga client for Kitsu.",
|
"description": "A self-hosted anime/manga client for Kitsu.",
|
||||||
"license":"MIT",
|
"license":"MIT",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/AnimeClient.php"
|
||||||
|
],
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Aviat\\AnimeClient\\": "src/"
|
"Aviat\\AnimeClient\\": "src/"
|
||||||
}
|
}
|
||||||
@ -20,7 +23,6 @@
|
|||||||
"aviat/banker": "^1.0.0",
|
"aviat/banker": "^1.0.0",
|
||||||
"aviat/ion": "1.0.*",
|
"aviat/ion": "1.0.*",
|
||||||
"filp/whoops": "^2.1.5",
|
"filp/whoops": "^2.1.5",
|
||||||
"guzzlehttp/guzzle": "^6.0",
|
|
||||||
"monolog/monolog": "^1.0",
|
"monolog/monolog": "^1.0",
|
||||||
"psr/http-message": "~1.0",
|
"psr/http-message": "~1.0",
|
||||||
"psr/log": "~1.0",
|
"psr/log": "~1.0",
|
||||||
|
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
14
index.php
14
index.php
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
|
use function Aviat\AnimeClient\loadToml;
|
||||||
|
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
use Whoops\Handler\PrettyPageHandler;
|
use Whoops\Handler\PrettyPageHandler;
|
||||||
use Whoops\Run;
|
use Whoops\Run;
|
||||||
@ -39,10 +41,11 @@ function _dir()
|
|||||||
|
|
||||||
// Define base directories
|
// Define base directories
|
||||||
$APP_DIR = _dir(__DIR__, 'app');
|
$APP_DIR = _dir(__DIR__, 'app');
|
||||||
|
$APPCONF_DIR = _dir($APP_DIR, 'appConf');
|
||||||
$CONF_DIR = _dir($APP_DIR, 'config');
|
$CONF_DIR = _dir($APP_DIR, 'config');
|
||||||
|
|
||||||
// Load composer autoloader
|
// Load composer autoloader
|
||||||
require _dir(__DIR__, '/vendor/autoload.php');
|
require _dir(__DIR__, 'vendor/autoload.php');
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Setup error handling
|
// Setup error handling
|
||||||
@ -54,18 +57,15 @@ $defaultHandler = new PrettyPageHandler();
|
|||||||
$whoops->pushHandler($defaultHandler);
|
$whoops->pushHandler($defaultHandler);
|
||||||
|
|
||||||
// Register as the error handler
|
// Register as the error handler
|
||||||
if (array_key_exists('whoops', $_GET))
|
|
||||||
{
|
|
||||||
$whoops->register();
|
$whoops->register();
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Dependency Injection setup
|
// Dependency Injection setup
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
require _dir($CONF_DIR, 'base_config.php'); // $base_config
|
require _dir($APPCONF_DIR, 'base_config.php'); // $base_config
|
||||||
$di = require _dir($APP_DIR, 'bootstrap.php');
|
$di = require _dir($APP_DIR, 'bootstrap.php');
|
||||||
|
|
||||||
$config = AnimeClient::loadToml($CONF_DIR);
|
$config = loadToml($CONF_DIR);
|
||||||
$config_array = array_merge($base_config, $config);
|
$config_array = array_merge($base_config, $config);
|
||||||
|
|
||||||
$container = $di($config_array);
|
$container = $di($config_array);
|
||||||
@ -78,5 +78,3 @@ unset($CONF_DIR);
|
|||||||
// Dispatch to the current route
|
// Dispatch to the current route
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$container->get('dispatcher')->__invoke();
|
$container->get('dispatcher')->__invoke();
|
||||||
|
|
||||||
// End of index.php
|
|
@ -160,7 +160,7 @@ class CSSMin extends BaseMin {
|
|||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
//Get config files
|
//Get config files
|
||||||
$config = require('../app/config/minify_config.php');
|
$config = require('../app/appConf/minify_config.php');
|
||||||
$groups = require($config['css_groups_file']);
|
$groups = require($config['css_groups_file']);
|
||||||
|
|
||||||
if ( ! array_key_exists($_GET['g'], $groups))
|
if ( ! array_key_exists($_GET['g'], $groups))
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
|
|
||||||
namespace Aviat\EasyMin;
|
namespace Aviat\EasyMin;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
use function Amp\wait;
|
||||||
use GuzzleHttp\Psr7\Request;
|
use Amp\Artax\{Client, FormBody, Request};
|
||||||
|
use Aviat\Ion\Json;
|
||||||
|
|
||||||
// Include guzzle
|
// Include guzzle
|
||||||
require_once('../vendor/autoload.php');
|
require_once('../vendor/autoload.php');
|
||||||
@ -97,14 +98,21 @@ class JSMin extends BaseMin {
|
|||||||
*/
|
*/
|
||||||
protected function closure_call(array $options)
|
protected function closure_call(array $options)
|
||||||
{
|
{
|
||||||
$client = new Client();
|
$formFields = http_build_query($options);
|
||||||
$response = $client->post('http://closure-compiler.appspot.com/compile', [
|
|
||||||
'headers' => [
|
$request = (new Request)
|
||||||
|
->setMethod('POST')
|
||||||
|
->setUri('http://closure-compiler.appspot.com/compile')
|
||||||
|
->setAllHeaders([
|
||||||
|
'Accept' => 'application/json',
|
||||||
'Accept-Encoding' => 'gzip',
|
'Accept-Encoding' => 'gzip',
|
||||||
'Content-type' => 'application/x-www-form-urlencoded'
|
'Content-type' => 'application/x-www-form-urlencoded'
|
||||||
],
|
])
|
||||||
'form_params' => $options
|
->setBody($formFields);
|
||||||
]);
|
|
||||||
|
$response = wait((new Client)->request($request, [
|
||||||
|
Client::OP_AUTO_ENCODING => false
|
||||||
|
]));
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
@ -118,13 +126,14 @@ class JSMin extends BaseMin {
|
|||||||
protected function check_minify_errors($options)
|
protected function check_minify_errors($options)
|
||||||
{
|
{
|
||||||
$error_res = $this->closure_call($options);
|
$error_res = $this->closure_call($options);
|
||||||
$error_json = (string)$error_res->getBody();
|
$error_json = $error_res->getBody();
|
||||||
$error_obj = json_decode($error_json) ?: (object)[];
|
$error_obj = Json::decode($error_json) ?: (object)[];
|
||||||
|
|
||||||
|
|
||||||
// Show error if exists
|
// Show error if exists
|
||||||
if ( ! empty($error_obj->errors) || ! empty($error_obj->serverErrors))
|
if ( ! empty($error_obj->errors) || ! empty($error_obj->serverErrors))
|
||||||
{
|
{
|
||||||
$error_json = json_encode($error_obj, JSON_PRETTY_PRINT);
|
$error_json = Json::encode($error_obj, JSON_PRETTY_PRINT);
|
||||||
header('Content-type: application/javascript');
|
header('Content-type: application/javascript');
|
||||||
echo "console.error(${error_json});";
|
echo "console.error(${error_json});";
|
||||||
die();
|
die();
|
||||||
@ -201,10 +210,11 @@ class JSMin extends BaseMin {
|
|||||||
// Now actually retrieve the compiled code
|
// Now actually retrieve the compiled code
|
||||||
$options['output_info'] = 'compiled_code';
|
$options['output_info'] = 'compiled_code';
|
||||||
$res = $this->closure_call($options);
|
$res = $this->closure_call($options);
|
||||||
$json = (string)$res->getBody();
|
$json = $res->getBody();
|
||||||
$obj = json_decode($json);
|
$obj = Json::decode($json);
|
||||||
|
|
||||||
return $obj->compiledCode;
|
//return $obj;
|
||||||
|
return $obj['compiledCode'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,7 +233,7 @@ class JSMin extends BaseMin {
|
|||||||
// ! Start Minifying
|
// ! Start Minifying
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
$config = require_once('../app/config/minify_config.php');
|
$config = require_once('../app/appConf/minify_config.php');
|
||||||
$groups = require_once($config['js_groups_file']);
|
$groups = require_once($config['js_groups_file']);
|
||||||
$cache_dir = "{$config['js_root']}cache";
|
$cache_dir = "{$config['js_root']}cache";
|
||||||
|
|
||||||
|
@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API;
|
namespace Aviat\AnimeClient\API;
|
||||||
|
|
||||||
|
use Amp;
|
||||||
use Amp\Artax\{
|
use Amp\Artax\{
|
||||||
Client,
|
Client,
|
||||||
FormBody,
|
FormBody,
|
||||||
Request
|
Request
|
||||||
};
|
};
|
||||||
use Aviat\Ion\Di\ContainerAware;
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
|
use Aviat\Ion\Json;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Psr\Log\LoggerAwareTrait;
|
use Psr\Log\LoggerAwareTrait;
|
||||||
|
|
||||||
@ -68,15 +70,30 @@ class APIRequestBuilder {
|
|||||||
protected $request;
|
protected $request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set body as form fields
|
* Set an authorization header
|
||||||
*
|
*
|
||||||
* @param array $fields Mapping of field names to values
|
* @param string $type The type of authorization, eg, basic, bearer, etc.
|
||||||
|
* @param string $value The authorization value
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function setFormFields(array $fields): self
|
public function setAuth(string $type, string $value): self
|
||||||
{
|
{
|
||||||
$body = $this->fixBody((new FormBody)->addFields($createData));
|
$authString = ucfirst($type) . ' ' . $value;
|
||||||
$this->setBody($body);
|
$this->setHeader('Authorization', $authString);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a basic authentication header
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $password
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setBasicAuth(string $username, string $password): self
|
||||||
|
{
|
||||||
|
$this->setAuth('basic', base64_encode($username . ':' . $password));
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +109,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 = (new FormBody)->addFields($fields);
|
||||||
|
$this->setBody($body);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a request header
|
* Set a request header
|
||||||
*
|
*
|
||||||
@ -123,6 +154,21 @@ class APIRequestBuilder {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the request body
|
||||||
|
*
|
||||||
|
* @param mixed $body
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setJsonBody($body): self
|
||||||
|
{
|
||||||
|
$requestBody = ( ! is_scalar($body))
|
||||||
|
? Json::encode($body)
|
||||||
|
: $body;
|
||||||
|
|
||||||
|
return $this->setBody($requestBody);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append a query string in array format
|
* Append a query string in array format
|
||||||
*
|
*
|
||||||
@ -143,6 +189,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 +222,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 +241,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))
|
||||||
{
|
{
|
||||||
@ -188,21 +251,6 @@ class APIRequestBuilder {
|
|||||||
$this->request->setUri($url);
|
$this->request->setUri($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the class state for a new request
|
* Reset the class state for a new request
|
||||||
*
|
*
|
||||||
|
@ -14,20 +14,10 @@
|
|||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\API\MAL;
|
namespace Aviat\AnimeClient\API;
|
||||||
|
|
||||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
use UnexpectedValueException;
|
||||||
|
|
||||||
class MALToKitsuTransformer extends AbstractTransformer {
|
class FailedResponseException extends UnexpectedValueException {
|
||||||
|
|
||||||
|
|
||||||
public function transform($item)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function untransform($item)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
<?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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base trait for api interaction
|
|
||||||
*/
|
|
||||||
trait GuzzleTrait {
|
|
||||||
/**
|
|
||||||
* The Guzzle http client object
|
|
||||||
* @var object
|
|
||||||
*/
|
|
||||||
protected $client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cookie jar object for api requests
|
|
||||||
* @var object
|
|
||||||
*/
|
|
||||||
protected $cookieJar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up the class properties
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
abstract protected function init();
|
|
||||||
}
|
|
@ -23,6 +23,10 @@ use Aviat\AnimeClient\API\Kitsu\Enum\{
|
|||||||
};
|
};
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
const AUTH_URL = 'https://kitsu.io/api/oauth/token';
|
||||||
|
const AUTH_USER_ID_KEY = 'kitsu-auth-userid';
|
||||||
|
const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data massaging helpers for the Kitsu API
|
* Data massaging helpers for the Kitsu API
|
||||||
*/
|
*/
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Kitsu;
|
namespace Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||||
|
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
use Aviat\AnimeClient\API\{
|
use Aviat\AnimeClient\API\{
|
||||||
CacheTrait,
|
CacheTrait,
|
||||||
@ -55,7 +57,7 @@ class Auth {
|
|||||||
$this->setContainer($container);
|
$this->setContainer($container);
|
||||||
$this->setCache($container->get('cache'));
|
$this->setCache($container->get('cache'));
|
||||||
$this->segment = $container->get('session')
|
$this->segment = $container->get('session')
|
||||||
->getSegment(AnimeClient::SESSION_SEGMENT);
|
->getSegment(SESSION_SEGMENT);
|
||||||
$this->model = $container->get('kitsu-model');
|
$this->model = $container->get('kitsu-model');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,16 +40,4 @@ class KitsuRequestBuilder extends APIRequestBuilder {
|
|||||||
'client_id' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
|
'client_id' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
|
||||||
'client_secret' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151',
|
'client_secret' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the request body
|
|
||||||
*
|
|
||||||
* @param array|FormBody|string $body
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public function setJsonBody(array $body): self
|
|
||||||
{
|
|
||||||
$requestBody = Json::encode($body);
|
|
||||||
return $this->setBody($requestBody);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -16,13 +16,14 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Kitsu;
|
namespace Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||||
|
|
||||||
|
use function Amp\wait;
|
||||||
|
|
||||||
|
use Amp\Artax\{Client, Request};
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
use Aviat\AnimeClient\API\GuzzleTrait;
|
|
||||||
use Aviat\AnimeClient\API\Kitsu as K;
|
use Aviat\AnimeClient\API\Kitsu as K;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Cookie\CookieJar;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
@ -34,37 +35,6 @@ trait KitsuTrait {
|
|||||||
*/
|
*/
|
||||||
protected $requestBuilder;
|
protected $requestBuilder;
|
||||||
|
|
||||||
/**
|
|
||||||
* The Guzzle http client object
|
|
||||||
* @var object
|
|
||||||
*/
|
|
||||||
protected $client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cookie jar object for api requests
|
|
||||||
* @var object
|
|
||||||
*/
|
|
||||||
protected $cookieJar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base url for api requests
|
|
||||||
* @var string $base_url
|
|
||||||
*/
|
|
||||||
protected $baseUrl = "https://kitsu.io/api/edge/";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP headers to send with every request
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $defaultHeaders = [
|
|
||||||
'User-Agent' => "Tim's Anime Client/4.0",
|
|
||||||
'Accept-Encoding' => 'application/vnd.api+json',
|
|
||||||
'Content-Type' => 'application/vnd.api+json',
|
|
||||||
'client_id' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
|
|
||||||
'client_secret' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the request builder object
|
* Set the request builder object
|
||||||
*
|
*
|
||||||
@ -78,30 +48,49 @@ trait KitsuTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the class properties
|
* Create a request object
|
||||||
*
|
*
|
||||||
* @return void
|
* @param string $type
|
||||||
|
* @param string $url
|
||||||
|
* @param array $options
|
||||||
|
* @return \Amp\Artax\Request
|
||||||
*/
|
*/
|
||||||
protected function init()
|
public function setUpRequest(string $type, string $url, array $options = []): Request
|
||||||
{
|
{
|
||||||
$defaults = [
|
$config = $this->container->get('config');
|
||||||
'cookies' => $this->cookieJar,
|
|
||||||
'headers' => $this->defaultHeaders,
|
|
||||||
'timeout' => 25,
|
|
||||||
'connect_timeout' => 25
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->cookieJar = new CookieJar();
|
$request = $this->requestBuilder->newRequest($type, $url);
|
||||||
$this->client = new Client([
|
|
||||||
'base_uri' => $this->baseUrl,
|
$sessionSegment = $this->getContainer()
|
||||||
'cookies' => TRUE,
|
->get('session')
|
||||||
'http_errors' => TRUE,
|
->getSegment(SESSION_SEGMENT);
|
||||||
'defaults' => $defaults
|
|
||||||
]);
|
if ($sessionSegment->get('auth_token') !== null && $url !== K::AUTH_URL)
|
||||||
|
{
|
||||||
|
$token = $sessionSegment->get('auth_token');
|
||||||
|
$request = $request->setAuth('bearer', $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('form_params', $options))
|
||||||
|
{
|
||||||
|
$request->setFormFields($options['form_params']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('query', $options))
|
||||||
|
{
|
||||||
|
$request->setQuery($options['query']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('body', $options))
|
||||||
|
{
|
||||||
|
$request->setJsonBody($options['body']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getFullRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a request via Guzzle
|
* Make a request
|
||||||
*
|
*
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @param string $url
|
* @param string $url
|
||||||
@ -110,48 +99,24 @@ trait KitsuTrait {
|
|||||||
*/
|
*/
|
||||||
private function getResponse(string $type, string $url, array $options = [])
|
private function getResponse(string $type, string $url, array $options = [])
|
||||||
{
|
{
|
||||||
$logger = null;
|
$request = $this->setUpRequest($type, $url, $options);
|
||||||
$validTypes = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
|
|
||||||
|
|
||||||
if ( ! in_array($type, $validTypes))
|
|
||||||
{
|
|
||||||
throw new InvalidArgumentException('Invalid http request type');
|
|
||||||
}
|
|
||||||
|
|
||||||
$defaultOptions = [
|
|
||||||
'headers' => $this->defaultHeaders
|
|
||||||
];
|
|
||||||
|
|
||||||
$logger = $this->container->getLogger('kitsu-request');
|
$logger = $this->container->getLogger('kitsu-request');
|
||||||
$sessionSegment = $this->getContainer()
|
|
||||||
->get('session')
|
|
||||||
->getSegment(AnimeClient::SESSION_SEGMENT);
|
|
||||||
|
|
||||||
if ($sessionSegment->get('auth_token') !== null && $url !== K::AUTH_URL)
|
$response = wait((new Client)->request($request));
|
||||||
{
|
|
||||||
$token = $sessionSegment->get('auth_token');
|
|
||||||
$defaultOptions['headers']['Authorization'] = "bearer {$token}";
|
|
||||||
}
|
|
||||||
|
|
||||||
$options = array_merge($defaultOptions, $options);
|
/* $logger->debug('Kitsu api response', [
|
||||||
|
'status' => $response->getStatus(),
|
||||||
$response = $this->client->request($type, $url, $options);
|
'reason' => $response->getReason(),
|
||||||
|
'body' => $response->getBody(),
|
||||||
$logger->debug('Kitsu API request', [
|
'headers' => $response->getAllHeaders(),
|
||||||
'requestParams' => [
|
'requestHeaders' => $request->getAllHeaders(),
|
||||||
'type' => $type,
|
]); */
|
||||||
'url' => $url,
|
|
||||||
],
|
|
||||||
'responseValues' => [
|
|
||||||
'status' => $response->getStatusCode()
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a request via Guzzle
|
* Make a request
|
||||||
*
|
*
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @param string $url
|
* @param string $url
|
||||||
@ -168,11 +133,11 @@ trait KitsuTrait {
|
|||||||
|
|
||||||
$response = $this->getResponse($type, $url, $options);
|
$response = $this->getResponse($type, $url, $options);
|
||||||
|
|
||||||
if ((int) $response->getStatusCode() > 299 || (int) $response->getStatusCode() < 200)
|
if ((int) $response->getStatus() > 299 || (int) $response->getStatus() < 200)
|
||||||
{
|
{
|
||||||
if ($logger)
|
if ($logger)
|
||||||
{
|
{
|
||||||
$logger->warning('Non 200 response for api call', $response->getBody());
|
$logger->warning('Non 200 response for api call', (array)$response->getBody());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +183,7 @@ trait KitsuTrait {
|
|||||||
$response = $this->getResponse('POST', ...$args);
|
$response = $this->getResponse('POST', ...$args);
|
||||||
$validResponseCodes = [200, 201];
|
$validResponseCodes = [200, 201];
|
||||||
|
|
||||||
if ( ! in_array((int) $response->getStatusCode(), $validResponseCodes))
|
if ( ! in_array((int) $response->getStatus(), $validResponseCodes))
|
||||||
{
|
{
|
||||||
if ($logger)
|
if ($logger)
|
||||||
{
|
{
|
||||||
@ -238,6 +203,6 @@ trait KitsuTrait {
|
|||||||
protected function deleteRequest(...$args): bool
|
protected function deleteRequest(...$args): bool
|
||||||
{
|
{
|
||||||
$response = $this->getResponse('DELETE', ...$args);
|
$response = $this->getResponse('DELETE', ...$args);
|
||||||
return ((int) $response->getStatusCode() === 204);
|
return ((int) $response->getStatus() === 204);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,11 +16,12 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Kitsu;
|
namespace Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||||
|
|
||||||
|
use Amp\Artax\Request;
|
||||||
use Aviat\AnimeClient\API\AbstractListItem;
|
use Aviat\AnimeClient\API\AbstractListItem;
|
||||||
use Aviat\Ion\Di\ContainerAware;
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use GuzzleHttp\Exception\ClientException;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,15 +31,24 @@ class ListItem extends AbstractListItem {
|
|||||||
use ContainerAware;
|
use ContainerAware;
|
||||||
use KitsuTrait;
|
use KitsuTrait;
|
||||||
|
|
||||||
public function __construct()
|
private function getAuthHeader()
|
||||||
{
|
{
|
||||||
$this->init();
|
$sessionSegment = $this->getContainer()
|
||||||
|
->get('session')
|
||||||
|
->getSegment(SESSION_SEGMENT);
|
||||||
|
|
||||||
|
if ($sessionSegment->get('auth_token') !== null)
|
||||||
|
{
|
||||||
|
$token = $sessionSegment->get('auth_token');
|
||||||
|
return "bearer {$token}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(array $data): bool
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(array $data): Request
|
||||||
{
|
{
|
||||||
$response = $this->getResponse('POST', 'library-entries', [
|
$body = [
|
||||||
'body' => Json::encode([
|
|
||||||
'data' => [
|
'data' => [
|
||||||
'type' => 'libraryEntries',
|
'type' => 'libraryEntries',
|
||||||
'attributes' => [
|
'attributes' => [
|
||||||
@ -60,29 +70,61 @@ class ListItem extends AbstractListItem {
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
])
|
];
|
||||||
]);
|
|
||||||
|
|
||||||
return ($response->getStatusCode() === 201);
|
$authHeader = $this->getAuthHeader();
|
||||||
|
|
||||||
|
$request = $this->requestBuilder->newRequest('POST', 'library-entries');
|
||||||
|
|
||||||
|
if ($authHeader !== FALSE)
|
||||||
|
{
|
||||||
|
$request = $request->setHeader('Authorization', $authHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(string $id): bool
|
return $request->setJsonBody($body)
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
// return ($response->getStatus() === 201);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(string $id): Request
|
||||||
{
|
{
|
||||||
$response = $this->getResponse('DELETE', "library-entries/{$id}");
|
$authHeader = $this->getAuthHeader();
|
||||||
return ($response->getStatusCode() === 204);
|
$request = $this->requestBuilder->newRequest('DELETE', "library-entries/{$id}");
|
||||||
|
|
||||||
|
if ($authHeader !== FALSE)
|
||||||
|
{
|
||||||
|
$request = $request->setHeader('Authorization', $authHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getFullRequest();
|
||||||
|
|
||||||
|
// return ($response->getStatus() === 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(string $id): array
|
public function get(string $id): array
|
||||||
{
|
{
|
||||||
return $this->getRequest("library-entries/{$id}", [
|
$authHeader = $this->getAuthHeader();
|
||||||
'query' => [
|
|
||||||
|
$request = $this->requestBuilder->newRequest('GET', "library-entries/{$id}")
|
||||||
|
->setQuery([
|
||||||
'include' => 'media,media.genres,media.mappings'
|
'include' => 'media,media.genres,media.mappings'
|
||||||
]
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($authHeader !== FALSE)
|
||||||
|
{
|
||||||
|
$request = $request->setHeader('Authorization', $authHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(string $id, array $data): Response
|
$request = $request->getFullRequest();
|
||||||
|
|
||||||
|
$response = \Amp\wait((new \Amp\Artax\Client)->request($request));
|
||||||
|
return Json::decode($response->getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(string $id, array $data): Request
|
||||||
{
|
{
|
||||||
|
$authHeader = $this->getAuthHeader();
|
||||||
$requestData = [
|
$requestData = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
@ -91,10 +133,14 @@ class ListItem extends AbstractListItem {
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = $this->getResponse('PATCH', "library-entries/{$id}", [
|
$request = $this->requestBuilder->newRequest('PATCH', "library-entries/{$id}")
|
||||||
'body' => JSON::encode($requestData)
|
->setJsonBody($requestData);
|
||||||
]);
|
|
||||||
|
|
||||||
return $response;
|
if ($authHeader !== FALSE)
|
||||||
|
{
|
||||||
|
$request = $request->setHeader('Authorization', $authHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getFullRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Kitsu;
|
namespace Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
|
use Amp\Artax\Request;
|
||||||
use Aviat\AnimeClient\API\CacheTrait;
|
use Aviat\AnimeClient\API\CacheTrait;
|
||||||
use Aviat\AnimeClient\API\JsonAPI;
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
use Aviat\AnimeClient\API\Kitsu as K;
|
use Aviat\AnimeClient\API\Kitsu as K;
|
||||||
@ -27,7 +28,6 @@ use Aviat\AnimeClient\API\Kitsu\Transformer\{
|
|||||||
};
|
};
|
||||||
use Aviat\Ion\Di\ContainerAware;
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use GuzzleHttp\Exception\ClientException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kitsu API Model
|
* Kitsu API Model
|
||||||
@ -72,9 +72,6 @@ class Model {
|
|||||||
*/
|
*/
|
||||||
public function __construct(ListItem $listItem)
|
public function __construct(ListItem $listItem)
|
||||||
{
|
{
|
||||||
// Set up Guzzle trait
|
|
||||||
$this->init();
|
|
||||||
|
|
||||||
$this->animeTransformer = new AnimeTransformer();
|
$this->animeTransformer = new AnimeTransformer();
|
||||||
$this->animeListTransformer = new AnimeListTransformer();
|
$this->animeListTransformer = new AnimeListTransformer();
|
||||||
$this->listItem = $listItem;
|
$this->listItem = $listItem;
|
||||||
@ -207,14 +204,55 @@ class Model {
|
|||||||
return $this->mangaTransformer->transform($baseData);
|
return $this->mangaTransformer->transform($baseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of anime list items
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getAnimeListCount() : int
|
||||||
|
{
|
||||||
|
$options = [
|
||||||
|
'query' => [
|
||||||
|
'filter' => [
|
||||||
|
'user_id' => $this->getUserIdByUsername(),
|
||||||
|
'media_type' => 'Anime'
|
||||||
|
],
|
||||||
|
'page' => [
|
||||||
|
'limit' => 1
|
||||||
|
],
|
||||||
|
'sort' => '-updated_at'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->getRequest('library-entries', $options);
|
||||||
|
|
||||||
|
return $response['meta']['count'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get and transform the entirety of the user's anime list
|
* Get and transform the entirety of the user's anime list
|
||||||
*
|
*
|
||||||
* @return array
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function getFullAnimeList(): array
|
public function getFullAnimeList(int $limit = 100, int $offset = 0): Request
|
||||||
{
|
{
|
||||||
|
$options = [
|
||||||
|
'query' => [
|
||||||
|
'filter' => [
|
||||||
|
'user_id' => $this->getUserIdByUsername($this->getUsername()),
|
||||||
|
'media_type' => 'Anime'
|
||||||
|
],
|
||||||
|
'include' => 'anime.mappings',
|
||||||
|
'page' => [
|
||||||
|
'offset' => $offset,
|
||||||
|
'limit' => $limit
|
||||||
|
],
|
||||||
|
'sort' => '-updated_at'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->setUpRequest('GET', 'library-entries', $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -355,9 +393,9 @@ class Model {
|
|||||||
* Create a list item
|
* Create a list item
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @return bool
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function createListItem(array $data): bool
|
public function createListItem(array $data): Request
|
||||||
{
|
{
|
||||||
$data['user_id'] = $this->getUserIdByUsername($this->getUsername());
|
$data['user_id'] = $this->getUserIdByUsername($this->getUsername());
|
||||||
return $this->listItem->create($data);
|
return $this->listItem->create($data);
|
||||||
@ -397,34 +435,20 @@ class Model {
|
|||||||
* Modify a list item
|
* Modify a list item
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @return array
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function updateListItem(array $data)
|
public function updateListItem(array $data): Request
|
||||||
{
|
{
|
||||||
try
|
return $this->listItem->update($data['id'], $data['data']);
|
||||||
{
|
|
||||||
$response = $this->listItem->update($data['id'], $data['data']);
|
|
||||||
return [
|
|
||||||
'statusCode' => $response->getStatusCode(),
|
|
||||||
'body' => $response->getBody(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
catch(ClientException $e)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'statusCode' => $e->getResponse()->getStatusCode(),
|
|
||||||
'body' => Json::decode((string)$e->getResponse()->getBody())
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a list item
|
* Remove a list item
|
||||||
*
|
*
|
||||||
* @param string $id - The id of the list item to remove
|
* @param string $id - The id of the list item to remove
|
||||||
* @return bool
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function deleteListItem(string $id): bool
|
public function deleteListItem(string $id): Request
|
||||||
{
|
{
|
||||||
return $this->listItem->delete($id);
|
return $this->listItem->delete($id);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API;
|
namespace Aviat\AnimeClient\API;
|
||||||
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
use Amp\Artax\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common interface for anime and manga list item CRUD
|
* Common interface for anime and manga list item CRUD
|
||||||
@ -29,7 +29,7 @@ interface ListItemInterface {
|
|||||||
* @param array $data -
|
* @param array $data -
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function create(array $data): bool;
|
public function create(array $data): Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a list item
|
* Retrieve a list item
|
||||||
@ -46,7 +46,7 @@ interface ListItemInterface {
|
|||||||
* @param array $data - The data with which to update the list item
|
* @param array $data - The data with which to update the list item
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function update(string $id, array $data): Response;
|
public function update(string $id, array $data): Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a list item
|
* Delete a list item
|
||||||
@ -54,5 +54,5 @@ interface ListItemInterface {
|
|||||||
* @param string $id - The id of the list item to delete
|
* @param string $id - The id of the list item to delete
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function delete(string $id): bool;
|
public function delete(string $id): Request;
|
||||||
}
|
}
|
@ -37,6 +37,14 @@ class MAL {
|
|||||||
KAWS::PLAN_TO_WATCH => AnimeWatchingStatus::PLAN_TO_WATCH
|
KAWS::PLAN_TO_WATCH => AnimeWatchingStatus::PLAN_TO_WATCH
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const MAL_KITSU_WATCHING_STATUS_MAP = [
|
||||||
|
1 => KAWS::WATCHING,
|
||||||
|
2 => KAWS::COMPLETED,
|
||||||
|
3 => KAWS::ON_HOLD,
|
||||||
|
4 => KAWS::DROPPED,
|
||||||
|
6 => KAWS::PLAN_TO_WATCH
|
||||||
|
];
|
||||||
|
|
||||||
public static function getIdToWatchingStatusMap()
|
public static function getIdToWatchingStatusMap()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\MAL;
|
namespace Aviat\AnimeClient\API\MAL;
|
||||||
|
|
||||||
use Amp\Artax\FormBody;
|
use Amp\Artax\{FormBody, Request};
|
||||||
use Aviat\AnimeClient\API\{
|
use Aviat\AnimeClient\API\{
|
||||||
AbstractListItem,
|
AbstractListItem,
|
||||||
XML
|
XML
|
||||||
@ -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): Request
|
||||||
{
|
{
|
||||||
$id = $data['id'];
|
$id = $data['id'];
|
||||||
$createData = [
|
$createData = [
|
||||||
@ -40,20 +40,26 @@ class ListItem {
|
|||||||
])
|
])
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = $this->getResponse('POST', "animelist/add/{$id}.xml", [
|
$config = $this->container->get('config');
|
||||||
'body' => $this->fixBody((new FormBody)->addFields($createData))
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $response->getBody() === 'Created';
|
return $this->requestBuilder->newRequest('POST', "animelist/add/{$id}.xml")
|
||||||
|
->setFormFields($createData)
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||||
|
->getFullRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(string $id): bool
|
public function delete(string $id): Request
|
||||||
{
|
{
|
||||||
$response = $this->getResponse('DELETE', "animelist/delete/{$id}.xml", [
|
$config = $this->container->get('config');
|
||||||
'body' => $this->fixBody((new FormBody)->addField('id', $id))
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $response->getBody() === 'Deleted';
|
return $this->requestBuilder->newRequest('DELETE', "animelist/delete/{$id}.xml")
|
||||||
|
->setFormFields([
|
||||||
|
'id' => $id
|
||||||
|
])
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
// return $response->getBody() === 'Deleted'
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(string $id): array
|
public function get(string $id): array
|
||||||
@ -61,15 +67,21 @@ class ListItem {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(string $id, array $data)
|
public function update(string $id, array $data): Request
|
||||||
{
|
{
|
||||||
|
$config = $this->container->get('config');
|
||||||
|
|
||||||
$xml = XML::toXML(['entry' => $data]);
|
$xml = XML::toXML(['entry' => $data]);
|
||||||
$body = (new FormBody)
|
$body = (new FormBody)
|
||||||
->addField('id', $id)
|
->addField('id', $id)
|
||||||
->addField('data', $xml);
|
->addField('data', $xml);
|
||||||
|
|
||||||
return $this->getResponse('POST', "animelist/update/{$id}.xml", [
|
return $this->requestBuilder->newRequest('POST', "animelist/update/{$id}.xml")
|
||||||
'body' => $this->fixBody($body)
|
->setFormFields([
|
||||||
]);
|
'id' => $id,
|
||||||
|
'data' => $xml
|
||||||
|
])
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||||
|
->getFullRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -85,51 +85,27 @@ trait MALTrait {
|
|||||||
* @param string $type
|
* @param string $type
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $options
|
* @param array $options
|
||||||
* @return \Amp\Promise
|
* @return \Amp\Artax\Response
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\MAL;
|
namespace Aviat\AnimeClient\API\MAL;
|
||||||
|
|
||||||
|
use Amp\Artax\Request;
|
||||||
use Aviat\AnimeClient\API\MAL as M;
|
use Aviat\AnimeClient\API\MAL as M;
|
||||||
use Aviat\AnimeClient\API\MAL\ListItem;
|
use Aviat\AnimeClient\API\MAL\ListItem;
|
||||||
use Aviat\AnimeClient\API\MAL\Transformer\AnimeListTransformer;
|
use Aviat\AnimeClient\API\MAL\Transformer\AnimeListTransformer;
|
||||||
@ -35,7 +36,7 @@ class Model {
|
|||||||
protected $animeListTransformer;
|
protected $animeListTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KitsuModel constructor.
|
* MAL Model constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(ListItem $listItem)
|
public function __construct(ListItem $listItem)
|
||||||
{
|
{
|
||||||
@ -43,7 +44,12 @@ class Model {
|
|||||||
$this->listItem = $listItem;
|
$this->listItem = $listItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createListItem(array $data): bool
|
public function createFullListItem(array $data): Request
|
||||||
|
{
|
||||||
|
return $this->listItem->create($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createListItem(array $data): Request
|
||||||
{
|
{
|
||||||
$createData = [
|
$createData = [
|
||||||
'id' => $data['id'],
|
'id' => $data['id'],
|
||||||
@ -69,7 +75,7 @@ class Model {
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $list;//['anime'];
|
return $list['myanimelist']['anime'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getListItem(string $listId): array
|
public function getListItem(string $listId): array
|
||||||
@ -77,13 +83,13 @@ class Model {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateListItem(array $data)
|
public function updateListItem(array $data): Request
|
||||||
{
|
{
|
||||||
$updateData = $this->animeListTransformer->untransform($data);
|
$updateData = $this->animeListTransformer->untransform($data);
|
||||||
return $this->listItem->update($updateData['id'], $updateData['data']);
|
return $this->listItem->update($updateData['id'], $updateData['data']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteListItem(string $id): bool
|
public function deleteListItem(string $id): Request
|
||||||
{
|
{
|
||||||
return $this->listItem->delete($id);
|
return $this->listItem->delete($id);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,12 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
AnimeWatchingStatus::PLAN_TO_WATCH => '6'
|
AnimeWatchingStatus::PLAN_TO_WATCH => '6'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform MAL episode data to Kitsu episode data
|
||||||
|
*
|
||||||
|
* @param array $item
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function transform($item)
|
public function transform($item)
|
||||||
{
|
{
|
||||||
$rewatching = (array_key_exists('rewatching', $item) && $item['rewatching']);
|
$rewatching = (array_key_exists('rewatching', $item) && $item['rewatching']);
|
||||||
@ -57,26 +63,42 @@ 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']))
|
$data =& $item['data'];
|
||||||
{
|
|
||||||
$map['data']['score'] = $item['data']['rating'] * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('status', $item['data']))
|
foreach($item['data'] as $key => $value)
|
||||||
{
|
{
|
||||||
$map['data']['status'] = self::statusMap[$item['data']['status']];
|
switch($key)
|
||||||
|
{
|
||||||
|
case 'notes':
|
||||||
|
$map['data']['comments'] = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'rating':
|
||||||
|
$map['data']['score'] = $value * 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'reconsuming':
|
||||||
|
$map['data']['enable_rewatching'] = (bool) $value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'reconsumeCount':
|
||||||
|
$map['data']['times_rewatched'] = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'status':
|
||||||
|
$map['data']['status'] = self::statusMap[$value];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return $map;
|
||||||
|
@ -20,11 +20,6 @@ use Yosymfony\Toml\Toml;
|
|||||||
|
|
||||||
define('SRC_DIR', realpath(__DIR__));
|
define('SRC_DIR', realpath(__DIR__));
|
||||||
|
|
||||||
/**
|
|
||||||
* Application constants
|
|
||||||
*/
|
|
||||||
class AnimeClient {
|
|
||||||
|
|
||||||
const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
|
const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
|
||||||
const DEFAULT_CONTROLLER_NAMESPACE = 'Aviat\AnimeClient\Controller';
|
const DEFAULT_CONTROLLER_NAMESPACE = 'Aviat\AnimeClient\Controller';
|
||||||
const DEFAULT_CONTROLLER = 'Aviat\AnimeClient\Controller\Anime';
|
const DEFAULT_CONTROLLER = 'Aviat\AnimeClient\Controller\Anime';
|
||||||
@ -39,7 +34,7 @@ class AnimeClient {
|
|||||||
* @param string $path - Path to load config
|
* @param string $path - Path to load config
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function loadToml(string $path): array
|
function loadToml(string $path): array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
$files = glob("{$path}/*.toml");
|
$files = glob("{$path}/*.toml");
|
||||||
@ -65,5 +60,3 @@ class AnimeClient {
|
|||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// End of AnimeClient.php
|
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\Command;
|
namespace Aviat\AnimeClient\Command;
|
||||||
|
|
||||||
|
use function Aviat\AnimeClient\loadToml;
|
||||||
|
|
||||||
use Aura\Session\SessionFactory;
|
use Aura\Session\SessionFactory;
|
||||||
use Aviat\AnimeClient\{
|
use Aviat\AnimeClient\{
|
||||||
AnimeClient,
|
AnimeClient,
|
||||||
@ -23,15 +25,9 @@ use Aviat\AnimeClient\{
|
|||||||
Util
|
Util
|
||||||
};
|
};
|
||||||
use Aviat\AnimeClient\API\CacheTrait;
|
use Aviat\AnimeClient\API\CacheTrait;
|
||||||
use Aviat\AnimeClient\API\Kitsu\{
|
use Aviat\AnimeClient\API\{Kitsu, MAL};
|
||||||
Auth as KitsuAuth,
|
use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder;
|
||||||
ListItem as KitsuListItem,
|
use Aviat\AnimeClient\API\MAL\MALRequestBuilder;
|
||||||
Model as KitsuModel
|
|
||||||
};
|
|
||||||
use Aviat\AnimeClient\API\MAL\{
|
|
||||||
ListItem as MALListItem,
|
|
||||||
Model as MALModel
|
|
||||||
};
|
|
||||||
use Aviat\Banker\Pool;
|
use Aviat\Banker\Pool;
|
||||||
use Aviat\Ion\Config;
|
use Aviat\Ion\Config;
|
||||||
use Aviat\Ion\Di\{Container, ContainerAware};
|
use Aviat\Ion\Di\{Container, ContainerAware};
|
||||||
@ -69,10 +65,11 @@ class BaseCommand extends Command {
|
|||||||
protected function setupContainer()
|
protected function setupContainer()
|
||||||
{
|
{
|
||||||
$APP_DIR = realpath(__DIR__ . '/../../app');
|
$APP_DIR = realpath(__DIR__ . '/../../app');
|
||||||
|
$APPCONF_DIR = realpath("{$APP_DIR}/appConf/");
|
||||||
$CONF_DIR = realpath("{$APP_DIR}/config/");
|
$CONF_DIR = realpath("{$APP_DIR}/config/");
|
||||||
require_once $CONF_DIR . '/base_config.php'; // $base_config
|
require_once $APPCONF_DIR . '/base_config.php'; // $base_config
|
||||||
|
|
||||||
$config = AnimeClient::loadToml($CONF_DIR);
|
$config = loadToml($CONF_DIR);
|
||||||
$config_array = array_merge($base_config, $config);
|
$config_array = array_merge($base_config, $config);
|
||||||
|
|
||||||
$di = function ($config_array) use ($APP_DIR) {
|
$di = function ($config_array) use ($APP_DIR) {
|
||||||
@ -84,10 +81,13 @@ class BaseCommand extends Command {
|
|||||||
|
|
||||||
$app_logger = new Logger('animeclient');
|
$app_logger = new Logger('animeclient');
|
||||||
$app_logger->pushHandler(new NullHandler);
|
$app_logger->pushHandler(new NullHandler);
|
||||||
$request_logger = new Logger('request');
|
$kitsu_request_logger = new Logger('kitsu-request');
|
||||||
$request_logger->pushHandler(new NullHandler);
|
$kitsu_request_logger->pushHandler(new NullHandler);
|
||||||
|
$mal_request_logger = new Logger('mal-request');
|
||||||
|
$mal_request_logger->pushHandler(new NullHandler);
|
||||||
$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');
|
||||||
|
|
||||||
// Create Config Object
|
// Create Config Object
|
||||||
$container->set('config', function() use ($config_array) {
|
$container->set('config', function() use ($config_array) {
|
||||||
@ -108,21 +108,35 @@ class BaseCommand extends Command {
|
|||||||
|
|
||||||
// Models
|
// Models
|
||||||
$container->set('kitsu-model', function($container) {
|
$container->set('kitsu-model', function($container) {
|
||||||
$listItem = new KitsuListItem();
|
$requestBuilder = new KitsuRequestBuilder();
|
||||||
|
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
|
||||||
|
|
||||||
|
$listItem = new Kitsu\ListItem();
|
||||||
$listItem->setContainer($container);
|
$listItem->setContainer($container);
|
||||||
$model = new KitsuModel($listItem);
|
$listItem->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
|
$model = new Kitsu\Model($listItem);
|
||||||
$model->setContainer($container);
|
$model->setContainer($container);
|
||||||
|
$model->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
$cache = $container->get('cache');
|
$cache = $container->get('cache');
|
||||||
$model->setCache($cache);
|
$model->setCache($cache);
|
||||||
return $model;
|
return $model;
|
||||||
});
|
});
|
||||||
$container->set('mal-model', function($container) {
|
$container->set('mal-model', function($container) {
|
||||||
$listItem = new MALListItem();
|
$requestBuilder = new MALRequestBuilder();
|
||||||
|
$requestBuilder->setLogger($container->getLogger('mal-request'));
|
||||||
|
|
||||||
|
$listItem = new MAL\ListItem();
|
||||||
$listItem->setContainer($container);
|
$listItem->setContainer($container);
|
||||||
$model = new MALModel($listItem);
|
$listItem->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
|
$model = new MAL\Model($listItem);
|
||||||
$model->setContainer($container);
|
$model->setContainer($container);
|
||||||
|
$model->setRequestBuilder($requestBuilder);
|
||||||
return $model;
|
return $model;
|
||||||
});
|
});
|
||||||
|
|
||||||
$container->set('util', function($container) {
|
$container->set('util', function($container) {
|
||||||
return new Util($container);
|
return new Util($container);
|
||||||
});
|
});
|
||||||
|
@ -16,8 +16,13 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\Command;
|
namespace Aviat\AnimeClient\Command;
|
||||||
|
|
||||||
|
use function Amp\{all, wait};
|
||||||
|
|
||||||
use Amp\Artax;
|
use Amp\Artax;
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
use Amp\Artax\Client;
|
||||||
|
use Aviat\AnimeClient\API\{JsonAPI, Kitsu, MAL};
|
||||||
|
use Aviat\AnimeClient\API\MAL\Transformer\AnimeListTransformer as ALT;
|
||||||
|
use Aviat\Ion\Json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the API Cache
|
* Clears the API Cache
|
||||||
@ -25,46 +30,7 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
class SyncKitsuWithMal extends BaseCommand {
|
class SyncKitsuWithMal extends BaseCommand {
|
||||||
|
|
||||||
protected $kitsuModel;
|
protected $kitsuModel;
|
||||||
|
protected $malModel;
|
||||||
public function getKitsuAnimeListPageCount()
|
|
||||||
{
|
|
||||||
$cacheItem = $this->cache->getItem(Kitsu::AUTH_TOKEN_CACHE_KEY);
|
|
||||||
|
|
||||||
$query = http_build_query([
|
|
||||||
'filter' => [
|
|
||||||
'user_id' => $this->kitsuModel->getUserIdByUsername(),
|
|
||||||
'media_type' => 'Anime'
|
|
||||||
],
|
|
||||||
'include' => 'anime,anime.genres,anime.mappings,anime.streamingLinks',
|
|
||||||
'page' => [
|
|
||||||
'limit' => 1
|
|
||||||
],
|
|
||||||
'sort' => '-updated_at'
|
|
||||||
]);
|
|
||||||
$request = (new Artax\Request)
|
|
||||||
->setUri("https://kitsu.io/api/edge/library-entries?{$query}")
|
|
||||||
->setProtocol('1.1')
|
|
||||||
->setAllHeaders([
|
|
||||||
'Accept' => 'application/vnd.api+json',
|
|
||||||
'Content-Type' => 'application/vnd.api+json',
|
|
||||||
'User-Agent' => "Tim's Anime Client/4.0"
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($cacheItem->isHit())
|
|
||||||
{
|
|
||||||
$token = $cacheItem->get();
|
|
||||||
$request->setHeader('Authorization', "bearer {$token}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->echoBox("WARNING: NOT LOGGED IN\nSome data might be missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = \Amp\wait((new Artax\Client)->request($request));
|
|
||||||
|
|
||||||
$body = json_decode($response->getBody(), TRUE);
|
|
||||||
return $body['meta']['count'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the image conversion script
|
* Run the image conversion script
|
||||||
@ -79,8 +45,216 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
$this->setContainer($this->setupContainer());
|
$this->setContainer($this->setupContainer());
|
||||||
$this->setCache($this->container->get('cache'));
|
$this->setCache($this->container->get('cache'));
|
||||||
$this->kitsuModel = $this->container->get('kitsu-model');
|
$this->kitsuModel = $this->container->get('kitsu-model');
|
||||||
|
$this->malModel = $this->container->get('mal-model');
|
||||||
|
|
||||||
|
$malCount = count($this->getMALList());
|
||||||
$kitsuCount = $this->getKitsuAnimeListPageCount();
|
$kitsuCount = $this->getKitsuAnimeListPageCount();
|
||||||
$this->echoBox("List item count: {$kitsuCount}");
|
|
||||||
|
$this->echoBox("Number of MAL list items: {$malCount}");
|
||||||
|
$this->echoBox("Number of Kitsu list items: {$kitsuCount}");
|
||||||
|
|
||||||
|
$data = $this->diffLists();
|
||||||
|
$this->echoBox("Number of items that need to be added to MAL: " . count($data));
|
||||||
|
|
||||||
|
if (! empty($data['addToMAL']))
|
||||||
|
{
|
||||||
|
$this->echoBox("Adding missing list items to MAL");
|
||||||
|
$this->createMALListItems($data['addToMAL']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKitsuList()
|
||||||
|
{
|
||||||
|
$count = $this->getKitsuAnimeListPageCount();
|
||||||
|
$size = 100;
|
||||||
|
$pages = ceil($count / $size);
|
||||||
|
|
||||||
|
$requests = [];
|
||||||
|
|
||||||
|
// Set up requests
|
||||||
|
for ($i = 0; $i < $count; $i++)
|
||||||
|
{
|
||||||
|
$offset = $i * $size;
|
||||||
|
$requests[] = $this->kitsuModel->getFullAnimeList($size, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
$promiseArray = (new Client())->requestMulti($requests);
|
||||||
|
|
||||||
|
$responses = wait(all($promiseArray));
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
foreach($responses as $response)
|
||||||
|
{
|
||||||
|
$data = Json::decode($response->getBody());
|
||||||
|
$output = array_merge_recursive($output, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMALList()
|
||||||
|
{
|
||||||
|
return $this->malModel->getFullList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filterMappings(array $includes): array
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
foreach($includes as $id => $mapping)
|
||||||
|
{
|
||||||
|
if ($mapping['externalSite'] === 'myanimelist/anime')
|
||||||
|
{
|
||||||
|
$output[$id] = $mapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2015-05-20T23:48:47.731Z
|
||||||
|
|
||||||
|
public function formatMALList()
|
||||||
|
{
|
||||||
|
$orig = $this->getMALList();
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
foreach($orig as $item)
|
||||||
|
{
|
||||||
|
$output[$item['series_animedb_id']] = [
|
||||||
|
'id' => $item['series_animedb_id'],
|
||||||
|
'data' => [
|
||||||
|
'status' => MAL::MAL_KITSU_WATCHING_STATUS_MAP[$item['my_status']],
|
||||||
|
'progress' => $item['my_watched_episodes'],
|
||||||
|
'reconsuming' => (bool) $item['my_rewatching'],
|
||||||
|
'reconsumeCount' => array_key_exists('times_rewatched', $item)
|
||||||
|
? $item['times_rewatched']
|
||||||
|
: 0,
|
||||||
|
// 'notes' => ,
|
||||||
|
'rating' => $item['my_score'],
|
||||||
|
'updatedAt' => (new \DateTime())
|
||||||
|
->setTimestamp((int)$item['my_last_updated'])
|
||||||
|
->format(\DateTime::W3C),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filterKitsuList()
|
||||||
|
{
|
||||||
|
$data = $this->getKitsuList();
|
||||||
|
$includes = JsonAPI::organizeIncludes($data['included']);
|
||||||
|
$includes['mappings'] = $this->filterMappings($includes['mappings']);
|
||||||
|
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
foreach($data['data'] as $listItem)
|
||||||
|
{
|
||||||
|
$animeId = $listItem['relationships']['anime']['data']['id'];
|
||||||
|
$potentialMappings = $includes['anime'][$animeId]['relationships']['mappings'];
|
||||||
|
$malId = null;
|
||||||
|
|
||||||
|
foreach ($potentialMappings as $mappingId)
|
||||||
|
{
|
||||||
|
if (array_key_exists($mappingId, $includes['mappings']))
|
||||||
|
{
|
||||||
|
$malId = $includes['mappings'][$mappingId]['externalId'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip to the next item if there isn't a MAL ID
|
||||||
|
if ($malId === null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$output[$listItem['id']] = [
|
||||||
|
'id' => $listItem['id'],
|
||||||
|
'malId' => $malId,
|
||||||
|
'data' => $listItem['attributes'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKitsuAnimeListPageCount()
|
||||||
|
{
|
||||||
|
return $this->kitsuModel->getAnimeListCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function diffLists()
|
||||||
|
{
|
||||||
|
// Get libraryEntries with media.mappings from Kitsu
|
||||||
|
// Organize mappings, and ignore entries without mappings
|
||||||
|
$kitsuList = $this->filterKitsuList();
|
||||||
|
|
||||||
|
// Get MAL list data
|
||||||
|
$malList = $this->formatMALList();
|
||||||
|
|
||||||
|
$itemsToAddToMAL = [];
|
||||||
|
|
||||||
|
foreach($kitsuList as $kitsuItem)
|
||||||
|
{
|
||||||
|
if (array_key_exists($kitsuItem['malId'], $malList))
|
||||||
|
{
|
||||||
|
// Eventually, compare the list entries, and determine which
|
||||||
|
// needs to be updated
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks like this item only exists on Kitsu
|
||||||
|
$itemsToAddToMAL[] = [
|
||||||
|
'mal_id' => $kitsuItem['malId'],
|
||||||
|
'data' => $kitsuItem['data']
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare each list entry
|
||||||
|
// If a list item exists only on MAL, create it on Kitsu with the existing data from MAL
|
||||||
|
// If a list item exists only on Kitsu, create it on MAL with the existing data from Kitsu
|
||||||
|
// If an item already exists on both APIS:
|
||||||
|
// Compare last updated dates, and use the later one
|
||||||
|
// Otherwise, use rewatch count, then episode progress as critera for selecting the more up
|
||||||
|
// to date entry
|
||||||
|
// Based on the 'newer' entry, update the other api list item
|
||||||
|
|
||||||
|
return [
|
||||||
|
'addToMAL' => $itemsToAddToMAL,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createMALListItems($itemsToAdd)
|
||||||
|
{
|
||||||
|
$transformer = new ALT();
|
||||||
|
$requests = [];
|
||||||
|
|
||||||
|
foreach($itemsToAdd as $item)
|
||||||
|
{
|
||||||
|
$data = $transformer->untransform($item);
|
||||||
|
$requests[] = $this->malModel->createFullListItem($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$promiseArray = (new Client())->requestMulti($requests);
|
||||||
|
|
||||||
|
$responses = wait(all($promiseArray));
|
||||||
|
|
||||||
|
foreach($responses as $key => $response)
|
||||||
|
{
|
||||||
|
$id = $itemsToAdd[$key]['mal_id'];
|
||||||
|
if ($response->getBody() === 'Created')
|
||||||
|
{
|
||||||
|
$this->echoBox("Successfully create list item with id: {$id}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->echoBox("Failed to create list item with id: {$id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||||
|
|
||||||
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
||||||
use Aviat\Ion\View\{HtmlView, HttpView, JsonView};
|
use Aviat\Ion\View\{HtmlView, HttpView, JsonView};
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
@ -102,7 +104,7 @@ class Controller {
|
|||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
|
||||||
$session = $container->get('session');
|
$session = $container->get('session');
|
||||||
$this->session = $session->getSegment(AnimeClient::SESSION_SEGMENT);
|
$this->session = $session->getSegment(SESSION_SEGMENT);
|
||||||
|
|
||||||
// Set a 'previous' flash value for better redirects
|
// Set a 'previous' flash value for better redirects
|
||||||
$server_params = $this->request->getServerParams();
|
$server_params = $this->request->getServerParams();
|
||||||
|
@ -16,9 +16,16 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\{
|
||||||
|
DEFAULT_CONTROLLER,
|
||||||
|
DEFAULT_CONTROLLER_NAMESPACE,
|
||||||
|
ERROR_MESSAGE_METHOD,
|
||||||
|
NOT_FOUND_METHOD,
|
||||||
|
SRC_DIR
|
||||||
|
};
|
||||||
|
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\Ion\Friend;
|
use Aviat\Ion\Friend;
|
||||||
use GuzzleHttp\Exception\ServerException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic routing/ dispatch
|
* Basic routing/ dispatch
|
||||||
@ -125,28 +132,13 @@ class Dispatcher extends RoutingBase {
|
|||||||
// If not route was matched, return an appropriate http
|
// If not route was matched, return an appropriate http
|
||||||
// error message
|
// error message
|
||||||
$error_route = $this->getErrorParams();
|
$error_route = $this->getErrorParams();
|
||||||
$controllerName = AnimeClient::DEFAULT_CONTROLLER;
|
$controllerName = DEFAULT_CONTROLLER;
|
||||||
$actionMethod = $error_route['action_method'];
|
$actionMethod = $error_route['action_method'];
|
||||||
$params = $error_route['params'];
|
$params = $error_route['params'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to catch API errors in a presentable fashion
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Actually instantiate the controller
|
|
||||||
$this->call($controllerName, $actionMethod, $params);
|
$this->call($controllerName, $actionMethod, $params);
|
||||||
}
|
}
|
||||||
catch (ServerException $e)
|
|
||||||
{
|
|
||||||
$response = $e->getResponse();
|
|
||||||
$this->call(AnimeClient::DEFAULT_CONTROLLER, AnimeClient::ERROR_MESSAGE_METHOD, [
|
|
||||||
$response->getStatusCode(),
|
|
||||||
'API Error',
|
|
||||||
'There was a problem getting data from an external source.',
|
|
||||||
(string) $response->getBody()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse out the arguments for the appropriate controller for
|
* Parse out the arguments for the appropriate controller for
|
||||||
@ -176,7 +168,7 @@ class Dispatcher extends RoutingBase {
|
|||||||
|
|
||||||
$action_method = (array_key_exists('action', $route->attributes))
|
$action_method = (array_key_exists('action', $route->attributes))
|
||||||
? $route->attributes['action']
|
? $route->attributes['action']
|
||||||
: AnimeClient::NOT_FOUND_METHOD;
|
: NOT_FOUND_METHOD;
|
||||||
|
|
||||||
$params = [];
|
$params = [];
|
||||||
if ( ! empty($route->__get('tokens')))
|
if ( ! empty($route->__get('tokens')))
|
||||||
@ -229,11 +221,11 @@ class Dispatcher extends RoutingBase {
|
|||||||
*/
|
*/
|
||||||
public function getControllerList()
|
public function getControllerList()
|
||||||
{
|
{
|
||||||
$default_namespace = AnimeClient::DEFAULT_CONTROLLER_NAMESPACE;
|
$default_namespace = DEFAULT_CONTROLLER_NAMESPACE;
|
||||||
$path = str_replace('\\', '/', $default_namespace);
|
$path = str_replace('\\', '/', $default_namespace);
|
||||||
$path = str_replace('Aviat/AnimeClient/', '', $path);
|
$path = str_replace('Aviat/AnimeClient/', '', $path);
|
||||||
$path = trim($path, '/');
|
$path = trim($path, '/');
|
||||||
$actual_path = realpath(_dir(AnimeClient::SRC_DIR, $path));
|
$actual_path = realpath(_dir(SRC_DIR, $path));
|
||||||
$class_files = glob("{$actual_path}/*.php");
|
$class_files = glob("{$actual_path}/*.php");
|
||||||
|
|
||||||
$controllers = [];
|
$controllers = [];
|
||||||
@ -285,7 +277,7 @@ class Dispatcher extends RoutingBase {
|
|||||||
$logger->info('Dispatcher - failed route');
|
$logger->info('Dispatcher - failed route');
|
||||||
$logger->info(print_r($failure, TRUE));
|
$logger->info(print_r($failure, TRUE));
|
||||||
|
|
||||||
$action_method = AnimeClient::ERROR_MESSAGE_METHOD;
|
$action_method = ERROR_MESSAGE_METHOD;
|
||||||
|
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
@ -308,7 +300,7 @@ class Dispatcher extends RoutingBase {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// Fall back to a 404 message
|
// Fall back to a 404 message
|
||||||
$action_method = AnimeClient::NOT_FOUND_METHOD;
|
$action_method = NOT_FOUND_METHOD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +329,7 @@ class Dispatcher extends RoutingBase {
|
|||||||
$controller_map = $this->getControllerList();
|
$controller_map = $this->getControllerList();
|
||||||
$controller_class = (array_key_exists($route_type, $controller_map))
|
$controller_class = (array_key_exists($route_type, $controller_map))
|
||||||
? $controller_map[$route_type]
|
? $controller_map[$route_type]
|
||||||
: AnimeClient::DEFAULT_CONTROLLER;
|
: DEFAULT_CONTROLLER;
|
||||||
|
|
||||||
if (array_key_exists($route_type, $controller_map))
|
if (array_key_exists($route_type, $controller_map))
|
||||||
{
|
{
|
||||||
|
@ -38,12 +38,6 @@ class API extends Model {
|
|||||||
*/
|
*/
|
||||||
protected $cache;
|
protected $cache;
|
||||||
|
|
||||||
/**
|
|
||||||
* Default settings for Guzzle
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $connectionDefaults = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\Model;
|
namespace Aviat\AnimeClient\Model;
|
||||||
|
|
||||||
|
use function Amp\some;
|
||||||
|
use function Amp\wait;
|
||||||
|
use Amp\Artax\Client;
|
||||||
use Aviat\AnimeClient\API\Kitsu\Enum\AnimeWatchingStatus;
|
use Aviat\AnimeClient\API\Kitsu\Enum\AnimeWatchingStatus;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
@ -91,9 +95,15 @@ class Anime extends API {
|
|||||||
return $this->kitsuModel->getAnime($slug);
|
return $this->kitsuModel->getAnime($slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAnimeById($anime_id)
|
/**
|
||||||
|
* Get anime by its kitsu id
|
||||||
|
*
|
||||||
|
* @param string $animeId
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAnimeById($animeId)
|
||||||
{
|
{
|
||||||
return $this->kitsuModel->getAnimeById($anime_id);
|
return $this->kitsuModel->getAnimeById($animeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,7 +114,6 @@ class Anime extends API {
|
|||||||
*/
|
*/
|
||||||
public function search($name)
|
public function search($name)
|
||||||
{
|
{
|
||||||
// $raw = $this->kitsuModel->search('anime', $name);
|
|
||||||
return $this->kitsuModel->search('anime', $name);
|
return $this->kitsuModel->search('anime', $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +137,8 @@ class Anime extends API {
|
|||||||
*/
|
*/
|
||||||
public function createLibraryItem(array $data): bool
|
public function createLibraryItem(array $data): bool
|
||||||
{
|
{
|
||||||
|
$requests = [];
|
||||||
|
|
||||||
if ($this->useMALAPI)
|
if ($this->useMALAPI)
|
||||||
{
|
{
|
||||||
$malData = $data;
|
$malData = $data;
|
||||||
@ -136,11 +147,17 @@ class Anime extends API {
|
|||||||
if ( ! is_null($malId))
|
if ( ! is_null($malId))
|
||||||
{
|
{
|
||||||
$malData['id'] = $malId;
|
$malData['id'] = $malId;
|
||||||
$this->malModel->createListItem($malData);
|
$requests['mal'] = $this->malModel->createListItem($malData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->kitsuModel->createListItem($data);
|
$requests['kitsu'] = $this->kitsuModel->createListItem($data);
|
||||||
|
|
||||||
|
$promises = (new Client)->requestMulti($requests);
|
||||||
|
|
||||||
|
$results = wait(some($promises));
|
||||||
|
|
||||||
|
return count($results[1]) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,12 +168,23 @@ class Anime extends API {
|
|||||||
*/
|
*/
|
||||||
public function updateLibraryItem(array $data): array
|
public function updateLibraryItem(array $data): array
|
||||||
{
|
{
|
||||||
|
$requests = [];
|
||||||
|
|
||||||
if ($this->useMALAPI)
|
if ($this->useMALAPI)
|
||||||
{
|
{
|
||||||
$this->malModel->updateListItem($data);
|
$requests['mal'] = $this->malModel->updateListItem($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->kitsuModel->updateListItem($data);
|
$requests['kitsu'] = $this->kitsuModel->updateListItem($data);
|
||||||
|
|
||||||
|
$promises = (new Client)->requestMulti($requests);
|
||||||
|
|
||||||
|
$results = wait(some($promises));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'body' => Json::decode($results[1]['kitsu']->getBody()),
|
||||||
|
'statusCode' => $results[1]['kitsu']->getStatus()
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,12 +196,18 @@ class Anime extends API {
|
|||||||
*/
|
*/
|
||||||
public function deleteLibraryItem(string $id, string $malId = null): bool
|
public function deleteLibraryItem(string $id, string $malId = null): bool
|
||||||
{
|
{
|
||||||
|
$requests = [];
|
||||||
|
|
||||||
if ($this->useMALAPI && ! is_null($malId))
|
if ($this->useMALAPI && ! is_null($malId))
|
||||||
{
|
{
|
||||||
$this->malModel->deleteListItem($malId);
|
$requests['mal'] = $this->malModel->deleteListItem($malId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->kitsuModel->deleteListItem($id);
|
$requests['kitsu'] = $this->kitsuModel->deleteListItem($id);
|
||||||
|
|
||||||
|
$results = wait(some((new Client)->requestMulti($requests)));
|
||||||
|
|
||||||
|
return count($results[1]) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End of AnimeModel.php
|
// End of AnimeModel.php
|
135
tests/API/APIRequestBuilderTest.php
Normal file
135
tests/API/APIRequestBuilderTest.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?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')
|
||||||
|
->setJsonBody($data)
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
$response = Amp\wait((new Client)->request($request));
|
||||||
|
$body = Json::decode($response->getBody());
|
||||||
|
|
||||||
|
$this->assertEquals($data, $body['json']);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?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;
|
namespace Aviat\AnimeClient\Tests\API;
|
||||||
|
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?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\Kitsu\Transformer;
|
namespace Aviat\AnimeClient\Tests\API\Kitsu\Transformer;
|
||||||
|
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?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\Kitsu\Transformer;
|
namespace Aviat\AnimeClient\Tests\API\Kitsu\Transformer;
|
||||||
|
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?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\Kitsu\Transformer;
|
namespace Aviat\AnimeClient\Tests\API\Kitsu\Transformer;
|
||||||
|
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?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\Kitsu\Transformer;
|
namespace Aviat\AnimeClient\Tests\API\Kitsu\Transformer;
|
||||||
|
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?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;
|
namespace Aviat\AnimeClient\Tests\API;
|
||||||
|
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?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;
|
namespace Aviat\AnimeClient\Tests\API;
|
||||||
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\SRC_DIR;
|
||||||
|
|
||||||
use Aura\Web\WebFactory;
|
use Aura\Web\WebFactory;
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Zend\Diactoros\{
|
use Zend\Diactoros\{
|
||||||
Response as HttpResponse,
|
Response as HttpResponse,
|
||||||
@ -23,7 +21,7 @@ define('TEST_VIEW_DIR', __DIR__ . '/test_views');
|
|||||||
class AnimeClient_TestCase extends TestCase {
|
class AnimeClient_TestCase extends TestCase {
|
||||||
// Test directory constants
|
// Test directory constants
|
||||||
const ROOT_DIR = ROOT_DIR;
|
const ROOT_DIR = ROOT_DIR;
|
||||||
const SRC_DIR = AnimeClient::SRC_DIR;
|
const SRC_DIR = SRC_DIR;
|
||||||
const TEST_DATA_DIR = TEST_DATA_DIR;
|
const TEST_DATA_DIR = TEST_DATA_DIR;
|
||||||
const TEST_VIEW_DIR = TEST_VIEW_DIR;
|
const TEST_VIEW_DIR = TEST_VIEW_DIR;
|
||||||
|
|
||||||
@ -159,25 +157,5 @@ class AnimeClient_TestCase extends TestCase {
|
|||||||
|
|
||||||
return Json::decode($rawData);
|
return Json::decode($rawData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a mock guzzle client for testing
|
|
||||||
* api call methods
|
|
||||||
*
|
|
||||||
* @param int $code The status code
|
|
||||||
* @param array $headers
|
|
||||||
* @param string $body
|
|
||||||
* @return Client
|
|
||||||
*/
|
|
||||||
public function getMockClient($code, $headers, $body)
|
|
||||||
{
|
|
||||||
$mock = new MockHandler([
|
|
||||||
new Response($code, $headers, $body)
|
|
||||||
]);
|
|
||||||
$handler = HandlerStack::create($mock);
|
|
||||||
$client = new Client(['handler' => $handler]);
|
|
||||||
|
|
||||||
return $client;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// End of AnimeClient_TestCase.php
|
// End of AnimeClient_TestCase.php
|
Loading…
Reference in New Issue
Block a user