diff --git a/README.md b/README.md index f2e9de8..f21107c 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,63 @@ AnimeNewsNetwork.com API browser ================================ + +Installation +------------ + +Pretty simple with [Composer](http://packagist.org), run: + +```sh +composer require anime-db/anime-news-network-browser-bundle +``` + +Add AnimeDbAnimeNewsNetworkBrowserBundle to your application kernel + +```php +// app/appKernel.php + +public function registerBundles() +{ + $bundles = array( + // ... + new AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\AnimeDbAnimeNewsNetworkBrowserBundle(), + ); +} +``` + +Configuration +------------- + +```yml +anime_db_anime_news_network_browser: + # Host name + # As a default used 'https://cdn.animenewsnetwork.com' + host: 'https://cdn.animenewsnetwork.com' + + # Reports + # As a default used '/encyclopedia/reports.xml' + reports: '/encyclopedia/reports.xml' + + # Anime/Manga Details + # As a default used '/encyclopedia/api.xml' + details: '/encyclopedia/api.xml' + + # HTTP User-Agent + # No default value + client: 'My Custom Bot 1.0' +``` + +Usage +----- + +First get browser + +```php +$browser = $this->get('anime_db.anime_news_network.browser'); +``` + +License +------- + +This bundle is under the [GPL v3 license](http://opensource.org/licenses/GPL-3.0). +See the complete license in the file: LICENSE diff --git a/src/AnimeDbAnimeNewsNetworkBrowserBundle.php b/src/AnimeDbAnimeNewsNetworkBrowserBundle.php new file mode 100644 index 0000000..7b5b663 --- /dev/null +++ b/src/AnimeDbAnimeNewsNetworkBrowserBundle.php @@ -0,0 +1,17 @@ + + * @copyright Copyright (c) 2011, Peter Gribanov + * @license http://opensource.org/licenses/GPL-3.0 GPL v3 + */ + +namespace AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AnimeDbAnimeNewsNetworkBrowserBundle extends Bundle +{ +} diff --git a/src/DependencyInjection/AnimeDbAnimeNewsNetworkBrowserExtension.php b/src/DependencyInjection/AnimeDbAnimeNewsNetworkBrowserExtension.php new file mode 100644 index 0000000..a404c3d --- /dev/null +++ b/src/DependencyInjection/AnimeDbAnimeNewsNetworkBrowserExtension.php @@ -0,0 +1,38 @@ + + * @copyright Copyright (c) 2011, Peter Gribanov + * @license http://opensource.org/licenses/GPL-3.0 GPL v3 + */ + +namespace AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader; + +class AnimeDbAnimeNewsNetworkBrowserExtension extends Extension +{ + /** + * @param array $configs + * @param ContainerBuilder $container + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + + $config = $this->processConfiguration(new Configuration(), $configs); + + $container->getDefinition('anime_db.anime_news_network.browser') + ->replaceArgument(1, $config['host']) + ->replaceArgument(2, $config['reports']) + ->replaceArgument(3, $config['details']) + ->replaceArgument(4, $config['client']) + ; + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 0000000..126f123 --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,57 @@ + + * @copyright Copyright (c) 2011, Peter Gribanov + * @license http://opensource.org/licenses/GPL-3.0 GPL v3 + */ + +namespace AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class Configuration implements ConfigurationInterface +{ + /** + * Config tree builder. + * + * Example config: + * + * anime_db_anime_news_network_browser: + * host: 'https://cdn.animenewsnetwork.com' + * reports: '/encyclopedia/reports.xml' + * details: '/encyclopedia/api.xml' + * client: 'My Custom Bot 1.0' + * + * @return ArrayNodeDefinition + */ + public function getConfigTreeBuilder() + { + return (new TreeBuilder()) + ->root('anime_db_anime_news_network_browser') + ->children() + ->scalarNode('host') + ->defaultValue('https://cdn.animenewsnetwork.com') + ->cannotBeEmpty() + ->end() + ->scalarNode('reports') + ->defaultValue('/encyclopedia/reports.xml') + ->cannotBeEmpty() + ->end() + ->scalarNode('details') + ->defaultValue('/encyclopedia/api.xml') + ->cannotBeEmpty() + ->end() + ->scalarNode('client') + ->defaultValue('') + ->cannotBeEmpty() + ->end() + ->end() + ->end() + ; + } +} diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml new file mode 100644 index 0000000..9a29627 --- /dev/null +++ b/src/Resources/config/services.yml @@ -0,0 +1,8 @@ +services: + anime_db.anime_news_network.browser: + class: AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\Service\Browser + arguments: [ '@anime_db.anime_news_network.browser.client', ~, ~, ~, ~ ] + + anime_db.anime_news_network.browser.client: + class: GuzzleHttp\Client + public: false diff --git a/src/Service/Browser.php b/src/Service/Browser.php new file mode 100644 index 0000000..205e3f3 --- /dev/null +++ b/src/Service/Browser.php @@ -0,0 +1,100 @@ + + * @copyright Copyright (c) 2011, Peter Gribanov + * @license http://opensource.org/licenses/GPL-3.0 GPL v3 + */ + +namespace AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\Service; + +use GuzzleHttp\Client; + +class Browser +{ + /** + * @var Client + */ + private $client; + + /** + * @var string + */ + private $host; + + /** + * @var string + */ + private $reports; + + /** + * @var string + */ + private $details; + + /** + * @var string + */ + private $app_client; + + /** + * @param Client $client + * @param string $host + * @param string $reports + * @param string $details + * @param string $app_client + */ + public function __construct(Client $client, $host, $reports, $details, $app_client) + { + $this->client = $client; + $this->host = $host; + $this->reports = $reports; + $this->details = $details; + $this->app_client = $app_client; + } + + /** + * @param int $id + * @param array $options + * + * @return string + */ + public function reports($id, array $options = []) + { + $options['id'] = $id; + + return $this->request($this->host.$this->reports, $options); + } + + /** + * @param array $options + * + * @return string + */ + public function details(array $options) + { + return $this->request($this->host.$this->details, $options); + } + + /** + * @param string $url + * @param array $options + * + * @return string + */ + private function request($url, array $options) + { + if ($this->app_client) { + $options['headers'] = array_merge( + ['User-Agent' => $this->app_client], + isset($options['headers']) ? $options['headers'] : [] + ); + } + + $response = $this->client->request('GET', $url, $options); + + return $response->getBody()->getContents(); + } +} diff --git a/tests/DependencyInjection/AnimeDbAnimeNewsNetworkBrowserExtensionTest.php b/tests/DependencyInjection/AnimeDbAnimeNewsNetworkBrowserExtensionTest.php new file mode 100644 index 0000000..83e3883 --- /dev/null +++ b/tests/DependencyInjection/AnimeDbAnimeNewsNetworkBrowserExtensionTest.php @@ -0,0 +1,111 @@ + + * @copyright Copyright (c) 2011, Peter Gribanov + * @license http://opensource.org/licenses/GPL-3.0 GPL v3 + */ + +namespace AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\Tests\DependencyInjection; + +use AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\DependencyInjection\AnimeDbAnimeNewsNetworkBrowserExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +class AnimeDbAnimeNewsNetworkBrowserExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ContainerBuilder + */ + private $container; + + /** + * @var AnimeDbAnimeNewsNetworkBrowserExtension + */ + private $extension; + + protected function setUp() + { + $this->container = $this->getMock(ContainerBuilder::class); + $this->extension = new AnimeDbAnimeNewsNetworkBrowserExtension(); + } + + /** + * @return array + */ + public function config() + { + return [ + [ + [], + 'https://cdn.animenewsnetwork.com', + '/encyclopedia/reports.xml', + '/encyclopedia/api.xml', + '', + ], + [ + [ + 'anime_db_anime_news_network_browser' => [ + 'host' => 'http://cdn.animenewsnetwork.com', + 'reports' => '/encyclopedia/reports.json', + 'details' => '/encyclopedia/api.json', + 'client' => 'My Custom Bot 1.0', + ], + ], + 'http://cdn.animenewsnetwork.com', + '/encyclopedia/reports.json', + '/encyclopedia/api.json', + 'My Custom Bot 1.0', + ], + ]; + } + + /** + * @dataProvider config + * + * @param array $config + * @param string $host + * @param string $reports + * @param string $details + * @param string $client + */ + public function testLoad(array $config, $host, $reports, $details, $client) + { + $browser = $this->getMock(Definition::class); + $browser + ->expects($this->at(0)) + ->method('replaceArgument') + ->with(1, $host) + ->will($this->returnSelf()) + ; + $browser + ->expects($this->at(1)) + ->method('replaceArgument') + ->with(2, $reports) + ->will($this->returnSelf()) + ; + $browser + ->expects($this->at(2)) + ->method('replaceArgument') + ->with(3, $details) + ->will($this->returnSelf()) + ; + $browser + ->expects($this->at(3)) + ->method('replaceArgument') + ->with(4, $client) + ->will($this->returnSelf()) + ; + + $this->container + ->expects($this->once()) + ->method('getDefinition') + ->with('anime_db.anime_news_network.browser') + ->will($this->returnValue($browser)) + ; + + $this->extension->load($config, $this->container); + } +} diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..7107f95 --- /dev/null +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,64 @@ + + * @copyright Copyright (c) 2011, Peter Gribanov + * @license http://opensource.org/licenses/GPL-3.0 GPL v3 + */ + +namespace AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\Tests\DependencyInjection; + +use AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\DependencyInjection\Configuration; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ScalarNode; + +class ConfigurationTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Configuration + */ + private $configuration; + + protected function setUp() + { + $this->configuration = new Configuration(); + } + + public function testConfigTree() + { + $tree_builder = $this->configuration->getConfigTreeBuilder(); + + $this->assertInstanceOf(TreeBuilder::class, $tree_builder); + + /* @var $tree ArrayNode */ + $tree = $tree_builder->buildTree(); + + $this->assertInstanceOf(ArrayNode::class, $tree); + $this->assertEquals('anime_db_anime_news_network_browser', $tree->getName()); + + /* @var $children ScalarNode[] */ + $children = $tree->getChildren(); + + $this->assertInternalType('array', $children); + $this->assertEquals(['host', 'reports', 'details', 'client'], array_keys($children)); + + $this->assertInstanceOf(ScalarNode::class, $children['host']); + $this->assertEquals('https://cdn.animenewsnetwork.com', $children['host']->getDefaultValue()); + $this->assertFalse($children['host']->isRequired()); + + $this->assertInstanceOf(ScalarNode::class, $children['reports']); + $this->assertEquals('/encyclopedia/reports.xml', $children['reports']->getDefaultValue()); + $this->assertFalse($children['reports']->isRequired()); + + $this->assertInstanceOf(ScalarNode::class, $children['details']); + $this->assertEquals('/encyclopedia/api.xml', $children['details']->getDefaultValue()); + $this->assertFalse($children['details']->isRequired()); + + $this->assertInstanceOf(ScalarNode::class, $children['client']); + $this->assertEquals('', $children['client']->getDefaultValue()); + $this->assertFalse($children['client']->isRequired()); + } +} diff --git a/tests/Service/BrowserTest.php b/tests/Service/BrowserTest.php new file mode 100644 index 0000000..e4931bd --- /dev/null +++ b/tests/Service/BrowserTest.php @@ -0,0 +1,167 @@ + + * @copyright Copyright (c) 2011, Peter Gribanov + * @license http://opensource.org/licenses/GPL-3.0 GPL v3 + */ + +namespace AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\Tests\Service; + +use AnimeDb\Bundle\AnimeNewsNetworkBrowserBundle\Service\Browser; +use GuzzleHttp\Client as HttpClient; +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\StreamInterface; + +class BrowserTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + private $host = 'example.org'; + + /** + * @var string + */ + private $reports = '/encyclopedia/reports.xml'; + + /** + * @var string + */ + private $details = '/encyclopedia/api.xml'; + + /** + * @var string + */ + private $app_client = 'My Custom Bot 1.0'; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|HttpClient + */ + private $client; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|StreamInterface + */ + private $stream; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|MessageInterface + */ + private $message; + + /** + * @var Browser + */ + private $browser; + + protected function setUp() + { + $this->client = $this->getMock(HttpClient::class); + $this->stream = $this->getMock(StreamInterface::class); + $this->message = $this->getMock(MessageInterface::class); + + $this->browser = new Browser($this->client, $this->host, $this->reports, $this->details, $this->app_client); + } + + /** + * @return array + */ + public function appClients() + { + return [ + [''], + ['Override User Agent'], + ]; + } + + /** + * @dataProvider appClients + * + * @param string $app_client + */ + public function testReports($app_client) + { + $id = 155; + $params = ['bar' => 'baz']; + $options = $params + [ + 'id' => $id, + 'headers' => [ + 'User-Agent' => $this->app_client, + ], + ]; + + if ($app_client) { + $options['headers']['User-Agent'] = $app_client; + $params['headers']['User-Agent'] = $app_client; + } + + $content = 'Hello, world!'; + + $this->stream + ->expects($this->once()) + ->method('getContents') + ->will($this->returnValue($content)) + ; + + $this->message + ->expects($this->once()) + ->method('getBody') + ->will($this->returnValue($this->stream)) + ; + + $this->client + ->expects($this->once()) + ->method('request') + ->with('GET', $this->host.$this->reports, $options) + ->will($this->returnValue($this->message)) + ; + + $this->assertEquals($content, $this->browser->reports($id, $params)); + } + + /** + * @dataProvider appClients + * + * @param string $app_client + */ + public function testDetails($app_client) + { + $params = ['bar' => 'baz']; + $options = $params + [ + 'headers' => [ + 'User-Agent' => $this->app_client, + ], + ]; + + if ($app_client) { + $options['headers']['User-Agent'] = $app_client; + $params['headers']['User-Agent'] = $app_client; + } + + $content = 'Hello, world!'; + + $this->stream + ->expects($this->once()) + ->method('getContents') + ->will($this->returnValue($content)) + ; + + $this->message + ->expects($this->once()) + ->method('getBody') + ->will($this->returnValue($this->stream)) + ; + + $this->client + ->expects($this->once()) + ->method('request') + ->with('GET', $this->host.$this->details, $options) + ->will($this->returnValue($this->message)) + ; + + $this->assertEquals($content, $this->browser->details($params)); + } +}