Version 5.1 - All the GraphQL #32

Closed
timw4mail wants to merge 1160 commits from develop into master
13 changed files with 213 additions and 37 deletions
Showing only changes of commit 7990b3ad68 - Show all commits

View File

@ -2,16 +2,16 @@
/** /**
* Hummingbird Anime List Client * Hummingbird Anime List Client
* *
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists * An API client for Kitsu to manage anime and manga watch lists
* *
* PHP version 7 * PHP version 8
* *
* @package HummingbirdAnimeClient * @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net> * @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2017 Timothy J. Warren * @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0 * @version 5.2
* @link https://github.com/timw4mail/HummingBirdAnimeClient * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/ */
use function Aviat\AnimeClient\loadConfig; use function Aviat\AnimeClient\loadConfig;
@ -21,12 +21,13 @@ use function Aviat\AnimeClient\loadConfig;
// //
// You shouldn't generally need to change anything below this line // You shouldn't generally need to change anything below this line
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
$APP_DIR = realpath(__DIR__ . '/../'); $APP_DIR = dirname(__DIR__);
$ROOT_DIR = realpath("{$APP_DIR}/../"); $ROOT_DIR = dirname($APP_DIR);
$tomlConfig = loadConfig(__DIR__); $tomlConfig = loadConfig(__DIR__);
return array_merge($tomlConfig, [ return array_merge($tomlConfig, [
'root' => $ROOT_DIR,
'asset_dir' => "{$ROOT_DIR}/public", 'asset_dir' => "{$ROOT_DIR}/public",
'base_config_dir' => __DIR__, 'base_config_dir' => __DIR__,
'config_dir' => "{$APP_DIR}/config", 'config_dir' => "{$APP_DIR}/config",

View File

@ -2,15 +2,15 @@
/** /**
* Hummingbird Anime List Client * Hummingbird Anime List Client
* *
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists * An API client for Kitsu to manage anime and manga watch lists
* *
* PHP version 7 * PHP version 8
* *
* @package HummingbirdAnimeClient * @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net> * @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2018 Timothy J. Warren * @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0 * @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/ */

View File

@ -34,11 +34,11 @@ use Psr\SimpleCache\CacheInterface;
use function Aviat\Ion\_dir; use function Aviat\Ion\_dir;
if ( ! defined('APP_DIR')) if ( ! defined('HB_APP_DIR'))
{ {
define('APP_DIR', __DIR__); define('HB_APP_DIR', __DIR__);
define('ROOT_DIR', dirname(APP_DIR)); define('ROOT_DIR', dirname(HB_APP_DIR));
define('TEMPLATE_DIR', _dir(APP_DIR, 'templates')); define('TEMPLATE_DIR', _dir(HB_APP_DIR, 'templates'));
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -50,7 +50,7 @@ return static function (array $configArray = []): Container {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Logging // Logging
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
$LOG_DIR = _dir(APP_DIR, 'logs'); $LOG_DIR = _dir(HB_APP_DIR, 'logs');
$appLogger = new Logger('animeclient'); $appLogger = new Logger('animeclient');
$appLogger->pushHandler(new RotatingFileHandler(_dir($LOG_DIR, 'app.log'), 2, Logger::WARNING)); $appLogger->pushHandler(new RotatingFileHandler(_dir($LOG_DIR, 'app.log'), 2, Logger::WARNING));

View File

@ -2,6 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
$file_patterns = [ $file_patterns = [
'app/appConf/*.php',
'app/bootstrap.php', 'app/bootstrap.php',
'migrations/*.php', 'migrations/*.php',
'src/**/*.php', 'src/**/*.php',
@ -16,7 +17,7 @@ if ( ! function_exists('glob_recursive'))
{ {
// Does not support flag GLOB_BRACE // Does not support flag GLOB_BRACE
function glob_recursive($pattern, $flags = 0) function glob_recursive(string $pattern, int $flags = 0): array
{ {
$files = glob($pattern, $flags); $files = glob($pattern, $flags);
@ -57,17 +58,21 @@ function get_text_to_replace(array $tokens): string
return $output; return $output;
} }
function get_tokens($source): array function get_tokens(string $source): array
{ {
return token_get_all($source); return token_get_all($source);
} }
function replace_files(array $files, $template) function replace_files(array $files, string $template): void
{ {
print_r($files); print_r($files);
foreach ($files as $file) foreach ($files as $file)
{ {
$source = file_get_contents($file); $source = file_get_contents($file);
if ($source === FALSE)
{
continue;
}
if (stripos($source, 'namespace') === FALSE) if (stripos($source, 'namespace') === FALSE)
{ {

View File

@ -4,6 +4,7 @@ parameters:
inferPrivatePropertyTypeFromConstructor: true inferPrivatePropertyTypeFromConstructor: true
level: 8 level: 8
paths: paths:
- app/appConf
- src - src
- ./console - ./console
- index.php - index.php

View File

@ -186,7 +186,7 @@ function checkFolderPermissions(ConfigInterface $config): array
$errors = []; $errors = [];
$publicDir = $config->get('asset_dir'); $publicDir = $config->get('asset_dir');
$APP_DIR = _dir(dirname(__DIR__, 2), '/app'); $APP_DIR = _dir($config->get('root'), 'app');
$pathMap = [ $pathMap = [
'app/config' => "{$APP_DIR}/config", 'app/config' => "{$APP_DIR}/config",
@ -211,7 +211,9 @@ function checkFolderPermissions(ConfigInterface $config): array
if ( ! $writable) if ( ! $writable)
{ {
// @codeCoverageIgnoreStart
$errors['writable'][] = $pretty; $errors['writable'][] = $pretty;
// @codeCoverageIgnoreEnd
} }
} }
@ -292,6 +294,7 @@ function getLocalImg (string $kitsuUrl, $webp = TRUE): string
/** /**
* Create a transparent placeholder image * Create a transparent placeholder image
* *
* @codeCoverageIgnore
* @param string $path * @param string $path
* @param int|null $width * @param int|null $width
* @param int|null $height * @param int|null $height
@ -378,7 +381,6 @@ function colNotEmpty(array $search, string $key): bool
* *
* @param CacheInterface $cache * @param CacheInterface $cache
* @return bool * @return bool
* @throws Throwable
*/ */
function clearCache(CacheInterface $cache): bool function clearCache(CacheInterface $cache): bool
{ {
@ -393,9 +395,7 @@ function clearCache(CacheInterface $cache): bool
$userData = array_filter((array)$userData, static fn ($value) => $value !== NULL); $userData = array_filter((array)$userData, static fn ($value) => $value !== NULL);
$cleared = $cache->clear(); $cleared = $cache->clear();
$saved = ( ! empty($userData)) $saved = ( ! empty($userData)) ? $cache->setMultiple($userData) : TRUE;
? $cache->setMultiple($userData)
: TRUE;
return $cleared && $saved; return $cleared && $saved;
} }

View File

@ -236,6 +236,9 @@ abstract class AbstractType implements ArrayAccess, Countable {
return TRUE; return TRUE;
} }
/**
* @codeCoverageIgnore
*/
final protected function fromObject(mixed $parent = null): float|null|bool|int|array|string final protected function fromObject(mixed $parent = null): float|null|bool|int|array|string
{ {
$object = $parent ?? $this; $object = $parent ?? $this;

View File

@ -32,6 +32,8 @@ class Config extends AbstractType {
// Settings in config.toml // Settings in config.toml
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
public string $root; // Path to app root
public ?string $asset_path; // Path to public folder for urls public ?string $asset_path; // Path to public folder for urls
/** /**
@ -62,8 +64,6 @@ class Config extends AbstractType {
/** /**
* Default list view type * Default list view type
* 'cover_view' or 'list_view' * 'cover_view' or 'list_view'
*
* @var string
*/ */
public ?string $default_view_type; public ?string $default_view_type;
@ -71,21 +71,13 @@ class Config extends AbstractType {
public bool $secure_urls = TRUE; public bool $secure_urls = TRUE;
/**
* @var string|bool
*/
public string|bool $show_anime_collection = FALSE; public string|bool $show_anime_collection = FALSE;
/**
* @var string|bool
*/
public string|bool $show_manga_collection = FALSE; public string|bool $show_manga_collection = FALSE;
/** /**
* CSS theme: light, dark, or auto-switching * CSS theme: light, dark, or auto-switching
* 'auto', 'light', or 'dark' * 'auto', 'light', or 'dark'
*
* @var string|null
*/ */
public ?string $theme = 'auto'; public ?string $theme = 'auto';

View File

@ -16,9 +16,11 @@
namespace Aviat\AnimeClient\Tests; namespace Aviat\AnimeClient\Tests;
use Amp\Http\Client\Response;
use function Aviat\AnimeClient\arrayToToml; use function Aviat\AnimeClient\arrayToToml;
use function Aviat\AnimeClient\checkFolderPermissions;
use function Aviat\AnimeClient\clearCache;
use function Aviat\AnimeClient\colNotEmpty;
use function Aviat\AnimeClient\getLocalImg;
use function Aviat\AnimeClient\getResponse; use function Aviat\AnimeClient\getResponse;
use function Aviat\AnimeClient\isSequentialArray; use function Aviat\AnimeClient\isSequentialArray;
use function Aviat\AnimeClient\tomlToArray; use function Aviat\AnimeClient\tomlToArray;
@ -89,4 +91,46 @@ class AnimeClientTest extends AnimeClientTestCase
{ {
$this->assertNotEmpty(getResponse('https://example.com')); $this->assertNotEmpty(getResponse('https://example.com'));
} }
public function testCheckFolderPermissions(): void
{
$config = $this->container->get('config');
$actual = checkFolderPermissions($config);
$this->assertTrue(is_array($actual));
}
public function testGetLocalImageEmptyUrl(): void
{
$actual = getLocalImg('');
$this->assertEquals('images/placeholder.webp', $actual);
}
public function testGetLocalImageBadUrl(): void
{
$actual = getLocalImg('//foo.bar');
$this->assertEquals('images/placeholder.webp', $actual);
}
public function testColNotEmpty(): void
{
$hasEmptyCols = [[
'foo' => '',
], [
'foo' => '',
]];
$hasNonEmptyCols = [[
'foo' => 'bar',
], [
'foo' => 'baz',
]];
$this->assertEquals(false, colNotEmpty($hasEmptyCols, 'foo'));
$this->assertEquals(true, colNotEmpty($hasNonEmptyCols, 'foo'));
}
public function testClearCache(): void
{
$this->assertTrue(clearCache($this->container->get('cache')));
}
} }

View File

@ -16,6 +16,7 @@
namespace Aviat\AnimeClient\Tests; namespace Aviat\AnimeClient\Tests;
use Aviat\Ion\Di\ContainerInterface;
use function Aviat\Ion\_dir; use function Aviat\Ion\_dir;
use Aviat\Ion\Json; use Aviat\Ion\Json;
@ -59,6 +60,7 @@ class AnimeClientTestCase extends TestCase {
parent::setUp(); parent::setUp();
$config_array = [ $config_array = [
'root' => self::ROOT_DIR,
'asset_path' => '/assets', 'asset_path' => '/assets',
'img_cache_path' => _dir(self::ROOT_DIR, 'public/images'), 'img_cache_path' => _dir(self::ROOT_DIR, 'public/images'),
'data_cache_path' => _dir(self::TEST_DATA_DIR, 'cache'), 'data_cache_path' => _dir(self::TEST_DATA_DIR, 'cache'),
@ -94,7 +96,7 @@ class AnimeClientTestCase extends TestCase {
]; ];
// Set up DI container // Set up DI container
$di = require _dir(self::ROOT_DIR, 'app', 'bootstrap.php'); $di = require self::ROOT_DIR . '/app/bootstrap.php';
$container = $di($config_array); $container = $di($config_array);
// Use mock session handler // Use mock session handler

View File

@ -0,0 +1,53 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient\Tests\Types;
use Aviat\AnimeClient\Types\Config;
use Aviat\AnimeClient\Types\UndefinedPropertyException;
class ConfigTest extends ConfigTestCase {
public function setUp(): void
{
parent::setUp();
$this->testClass = Config::class;
}
public function testSetMethods(): void
{
$type = $this->testClass::from([
'anilist' => [],
'cache' => [],
'database' => [],
]);
$this->assertEquals(3, $type->count());
}
public function testOffsetUnset(): void
{
$type = $this->testClass::from([
'anilist' => [],
]);
$this->assertTrue($type->offsetExists('anilist'));
$type->offsetUnset('anilist');
$this->assertNotTrue($type->offsetExists('anilist'));
}
}

View File

@ -0,0 +1,72 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient\Tests\Types;
use Aviat\AnimeClient\Tests\AnimeClientTestCase;
use Aviat\AnimeClient\Types\UndefinedPropertyException;
abstract class ConfigTestCase extends AnimeClientTestCase {
public string $testClass;
public function testCheck(): void
{
$result = $this->testClass::check([]);
$this->assertEquals([], $result);
}
public function testSetUndefinedProperty(): void
{
$this->expectException(UndefinedPropertyException::class);
$this->testClass::from([
'foobar' => 'baz',
]);
}
public function testToString(): void
{
$actual = $this->testClass::from([])->__toString();
$this->assertMatchesSnapshot($actual);
}
public function testOffsetExists(): void
{
$actual = $this->testClass::from([
'anilist' => [],
])->offsetExists('anilist');
$this->assertTrue($actual);
}
public function testSetState(): void
{
$normal = $this->testClass::from([]);
$setState = $this->testClass::__set_state([]);
$this->assertEquals($normal, $setState);
}
public function testIsEmpty(): void
{
$type = $this->testClass::from([]);
$this->assertTrue($type->isEmpty());
}
public function testCount(): void
{
$type = $this->testClass::from([]);
$this->assertEquals(0, $type->count());
}
}

View File

@ -0,0 +1,3 @@
Aviat\AnimeClient\Types\Config Object
(
)