Merge branch 'feature/29' into develop

This commit is contained in:
Alejandro Celaya 2016-08-12 18:01:38 +02:00
commit 2b2c0b7c13
16 changed files with 8 additions and 343 deletions

View File

@ -23,6 +23,7 @@
"zendframework/zend-i18n": "^2.7",
"mtymek/expressive-config-manager": "^0.4",
"acelaya/zsm-annotated-services": "^0.2.0",
"acelaya/ze-content-based-error-handler": "^1.0",
"doctrine/orm": "^2.5",
"guzzlehttp/guzzle": "^6.2",
"symfony/console": "^3.0",

View File

@ -1,5 +1,4 @@
<?php
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
use Zend\Expressive;
use Zend\Expressive\Container;
use Zend\Expressive\Router;
@ -17,7 +16,6 @@ return [
],
'aliases' => [
Router\RouterInterface::class => Router\FastRouteRouter::class,
'Zend\Expressive\FinalHandler' => ContentBasedErrorHandler::class,
],
],

View File

@ -1,5 +1,5 @@
<?php
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
use Acelaya\ExpressiveErrorHandler\ErrorHandler\ContentBasedErrorHandler;
use Zend\Expressive\Container\WhoopsErrorHandlerFactory;
return [

View File

@ -1,4 +1,5 @@
<?php
use Acelaya\ExpressiveErrorHandler\ConfigProvider as ErrorHandlerProvider;
use Shlinkio\Shlink\CLI;
use Shlinkio\Shlink\Common;
use Shlinkio\Shlink\Core;
@ -16,6 +17,7 @@ use Zend\Expressive\ConfigManager\ZendConfigProvider;
*/
return (new ConfigManager([
ErrorHandlerProvider::class,
Common\ConfigProvider::class,
Core\ConfigProvider::class,
CLI\ConfigProvider::class,

View File

@ -4,7 +4,6 @@ use Doctrine\Common\Cache\Cache;
use Doctrine\ORM\EntityManager;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Common\ErrorHandler;
use Shlinkio\Shlink\Common\Factory\CacheFactory;
use Shlinkio\Shlink\Common\Factory\EntityManagerFactory;
use Shlinkio\Shlink\Common\Factory\LoggerFactory;
@ -22,7 +21,6 @@ return [
EntityManager::class => EntityManagerFactory::class,
GuzzleHttp\Client::class => InvokableFactory::class,
Cache::class => CacheFactory::class,
LoggerInterface::class => LoggerFactory::class,
'Logger_Shlink' => LoggerFactory::class,
Translator::class => TranslatorFactory::class,
@ -30,17 +28,15 @@ return [
LocaleMiddleware::class => AnnotatedFactory::class,
IpLocationResolver::class => AnnotatedFactory::class,
ErrorHandler\ContentBasedErrorHandler::class => AnnotatedFactory::class,
ErrorHandler\ErrorHandlerManager::class => ErrorHandler\ErrorHandlerManagerFactory::class,
],
'aliases' => [
'em' => EntityManager::class,
'httpClient' => GuzzleHttp\Client::class,
'translator' => Translator::class,
'logger' => LoggerInterface::class,
Logger::class => LoggerInterface::class,
AnnotatedFactory::CACHE_SERVICE => Cache::class,
Logger::class => 'Logger_Shlink',
LoggerInterface::class => 'Logger_Shlink',
],
],

View File

@ -1,22 +0,0 @@
<?php
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
use Zend\Expressive\Container\TemplatedErrorHandlerFactory;
use Zend\Stratigility\FinalHandler;
return [
'error_handler' => [
'plugins' => [
'invokables' => [
'text/plain' => FinalHandler::class,
],
'factories' => [
ContentBasedErrorHandler::DEFAULT_CONTENT => TemplatedErrorHandlerFactory::class,
],
'aliases' => [
'application/xhtml+xml' => ContentBasedErrorHandler::DEFAULT_CONTENT,
],
],
],
];

View File

@ -1,76 +0,0 @@
<?php
namespace Shlinkio\Shlink\Common\ErrorHandler;
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
class ContentBasedErrorHandler implements ErrorHandlerInterface
{
const DEFAULT_CONTENT = 'text/html';
/**
* @var ErrorHandlerManagerInterface
*/
private $errorHandlerManager;
/**
* ContentBasedErrorHandler constructor.
* @param ErrorHandlerManagerInterface|ErrorHandlerManager $errorHandlerManager
*
* @Inject({ErrorHandlerManager::class})
*/
public function __construct(ErrorHandlerManagerInterface $errorHandlerManager)
{
$this->errorHandlerManager = $errorHandlerManager;
}
/**
* Final handler for an application.
*
* @param Request $request
* @param Response $response
* @param null|mixed $err
* @return Response
*/
public function __invoke(Request $request, Response $response, $err = null)
{
// Try to get an error handler for provided request accepted type
$errorHandler = $this->resolveErrorHandlerFromAcceptHeader($request);
return $errorHandler($request, $response, $err);
}
/**
* Tries to resolve
*
* @param Request $request
* @return callable
*/
protected function resolveErrorHandlerFromAcceptHeader(Request $request)
{
// Try to find an error handler for one of the accepted content types
$accepts = $request->hasHeader('Accept') ? $request->getHeaderLine('Accept') : self::DEFAULT_CONTENT;
$accepts = explode(',', $accepts);
foreach ($accepts as $accept) {
if (! $this->errorHandlerManager->has($accept)) {
continue;
}
return $this->errorHandlerManager->get($accept);
}
// If it wasn't possible to find an error handler for accepted content type, use default one if registered
if ($this->errorHandlerManager->has(self::DEFAULT_CONTENT)) {
return $this->errorHandlerManager->get(self::DEFAULT_CONTENT);
}
// It wasn't possible to find an error handler
throw new InvalidArgumentException(sprintf(
'It wasn\'t possible to find an error handler for ["%s"] content types. '
. 'Make sure you have registered at least the default "%s" content type',
implode('", "', $accepts),
self::DEFAULT_CONTENT
));
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace Shlinkio\Shlink\Common\ErrorHandler;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
interface ErrorHandlerInterface
{
/**
* Final handler for an application.
*
* @param Request $request
* @param Response $response
* @param null|mixed $err
* @return Response
*/
public function __invoke(Request $request, Response $response, $err = null);
}

View File

@ -1,21 +0,0 @@
<?php
namespace Shlinkio\Shlink\Common\ErrorHandler;
use Zend\ServiceManager\AbstractPluginManager;
use Zend\ServiceManager\Exception\InvalidServiceException;
class ErrorHandlerManager extends AbstractPluginManager implements ErrorHandlerManagerInterface
{
public function validate($instance)
{
if (is_callable($instance)) {
return;
}
throw new InvalidServiceException(sprintf(
'Only callables are valid plugins for "%s". "%s" provided',
__CLASS__,
is_object($instance) ? get_class($instance) : gettype($instance)
));
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace Shlinkio\Shlink\Common\ErrorHandler;
use Interop\Container\ContainerInterface;
use Interop\Container\Exception\ContainerException;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\FactoryInterface;
class ErrorHandlerManagerFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
* @return object
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$config = $container->get('config')['error_handler'];
$plugins = isset($config['plugins']) ? $config['plugins'] : [];
return new ErrorHandlerManager($container, $plugins);
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace Shlinkio\Shlink\Common\ErrorHandler;
use Interop\Container\ContainerInterface;
interface ErrorHandlerManagerInterface extends ContainerInterface
{
}

View File

@ -23,7 +23,6 @@ class ConfigProviderTest extends TestCase
{
$config = $this->configProvider->__invoke();
$this->assertArrayHasKey('error_handler', $config);
$this->assertArrayHasKey('middleware_pipeline', $config);
$this->assertArrayHasKey('dependencies', $config);
$this->assertArrayHasKey('twig', $config);

View File

@ -1,75 +0,0 @@
<?php
namespace ShlinkioTest\Shlink\Common\ErrorHandler;
use PHPUnit_Framework_TestCase as TestCase;
use Shlinkio\Shlink\Common\ErrorHandler\ContentBasedErrorHandler;
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManager;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;
use Zend\ServiceManager\ServiceManager;
class ContentBasedErrorHandlerTest extends TestCase
{
/**
* @var ContentBasedErrorHandler
*/
protected $errorHandler;
public function setUp()
{
$this->errorHandler = new ContentBasedErrorHandler(new ErrorHandlerManager(new ServiceManager(), [
'factories' => [
'text/html' => [$this, 'factory'],
'application/json' => [$this, 'factory'],
],
]));
}
public function factory($container, $name)
{
return function () use ($name) {
return $name;
};
}
/**
* @test
*/
public function correctAcceptHeaderValueInvokesErrorHandler()
{
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept', 'foo/bar,application/json');
$result = $this->errorHandler->__invoke($request, new Response());
$this->assertEquals('application/json', $result);
}
/**
* @test
*/
public function defaultContentTypeIsUsedWhenNoAcceptHeaderisPresent()
{
$request = ServerRequestFactory::fromGlobals();
$result = $this->errorHandler->__invoke($request, new Response());
$this->assertEquals('text/html', $result);
}
/**
* @test
*/
public function defaultContentTypeIsUsedWhenAcceptedContentIsNotSupported()
{
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept', 'foo/bar,text/xml');
$result = $this->errorHandler->__invoke($request, new Response());
$this->assertEquals('text/html', $result);
}
/**
* @test
* @expectedException \Shlinkio\Shlink\Common\Exception\InvalidArgumentException
*/
public function ifNoErrorHandlerIsFoundAnExceptionIsThrown()
{
$this->errorHandler = new ContentBasedErrorHandler(new ErrorHandlerManager(new ServiceManager(), []));
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept', 'foo/bar,text/xml');
$result = $this->errorHandler->__invoke($request, new Response());
}
}

View File

@ -1,35 +0,0 @@
<?php
namespace ShlinkioTest\Shlink\Common\ErrorHandler;
use PHPUnit_Framework_TestCase as TestCase;
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManager;
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManagerFactory;
use Zend\ServiceManager\ServiceManager;
class ErrorHandlerManagerFactoryTest extends TestCase
{
/**
* @var ErrorHandlerManagerFactory
*/
protected $factory;
public function setUp()
{
$this->factory = new ErrorHandlerManagerFactory();
}
/**
* @test
*/
public function serviceIsCreated()
{
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
'config' => [
'error_handler' => [
'plugins' => [],
],
],
]]), '');
$this->assertInstanceOf(ErrorHandlerManager::class, $instance);
}
}

View File

@ -1,45 +0,0 @@
<?php
namespace ShlinkioTest\Shlink\Common\ErrorHandler;
use PHPUnit_Framework_TestCase as TestCase;
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerManager;
use Zend\ServiceManager\ServiceManager;
class ErrorHandlerManagerTest extends TestCase
{
/**
* @var ErrorHandlerManager
*/
protected $pluginManager;
public function setUp()
{
$this->pluginManager = new ErrorHandlerManager(new ServiceManager(), [
'services' => [
'foo' => function () {
},
],
'invokables' => [
'invalid' => \stdClass::class,
]
]);
}
/**
* @test
*/
public function callablesAreReturned()
{
$instance = $this->pluginManager->get('foo');
$this->assertInstanceOf(\Closure::class, $instance);
}
/**
* @test
* @expectedException \Zend\ServiceManager\Exception\InvalidServiceException
*/
public function nonCallablesThrowException()
{
$this->pluginManager->get('invalid');
}
}

View File

@ -1,9 +1,9 @@
<?php
namespace Shlinkio\Shlink\Rest\ErrorHandler;
use Psr\Http\Message\ServerRequestInterface as Request;
use Acelaya\ExpressiveErrorHandler\ErrorHandler\ErrorHandlerInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Shlinkio\Shlink\Common\ErrorHandler\ErrorHandlerInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Expressive\Router\RouteResult;