Skip to content

Commit

Permalink
refact(listener): error response
Browse files Browse the repository at this point in the history
  • Loading branch information
euskadi31 committed Oct 21, 2015
1 parent 93127e9 commit 4ed20a6
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 36 deletions.
122 changes: 122 additions & 0 deletions src/Provider/Rest/Exception/ErrorCollectionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php
/*
* This file is part of the RestServiceProvider.
*
* (c) Axel Etcheverry <axel@etcheverry.biz>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Euskadi31\Silex\Provider\Rest\Exception;

use Symfony\Component\HttpKernel\Exception\HttpException;
use Iterator;
use ArrayAccess;
use Countable;

/**
* ErrorCollectionException
*
* @author Axel Etcheverry <axel@etcheverry.biz>
*/
class ErrorCollectionException extends HttpException implements Iterator, ArrayAccess, Countable
{
/**
* @var array
*/
private $container;

/**
*
* @param array $errors
* @param integer $statusCode
* @param array $headers
*/
public function __construct(array $errors, $statusCode = 400, array $headers = [])
{
parent::__construct($statusCode, null, null, $headers);

$this->container = $errors;
}

/**
* {@inheritDoc}
*/
public function count()
{
return count($this->container);
}

/**
* {@inheritDoc}
*/
public function offsetSet($offset, $value)
{
$this->container[] = $value;
}

/**
* {@inheritDoc}
*/
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}

/**
* {@inheritDoc}
*/
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}

/**
* {@inheritDoc}
*/
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}

/**
* {@inheritDoc}
*/
public function rewind()
{
return reset($this->container);
}

/**
* {@inheritDoc}
*/
public function current()
{
return current($this->container);
}

/**
* {@inheritDoc}
*/
public function key()
{
return key($this->container);
}

/**
* {@inheritDoc}
*/
public function next()
{
return next($this->container);
}

/**
* {@inheritDoc}
*/
public function valid()
{
return key($this->container) !== null;
}
}
21 changes: 21 additions & 0 deletions src/Provider/Rest/Exception/InvalidParameterExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the RestServiceProvider.
*
* (c) Axel Etcheverry <axel@etcheverry.biz>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Euskadi31\Silex\Provider\Rest\Exception;

/**
* InvalidParameterExceptionInterface
*
* @author Axel Etcheverry <axel@etcheverry.biz>
*/
interface InvalidParameterExceptionInterface
{
public function getParameter();
}
47 changes: 35 additions & 12 deletions src/Provider/Rest/RestListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,31 @@ public function onKernelRequest(GetResponseEvent $event)
$request->attributes->set('_locale', $request->getPreferredLanguage());
}

/**
* Process exception
*
* @param \Exception $exception
* @return array
*/
protected function processException(\Exception $exception)
{
$error = [
'message' => $exception->getMessage(),
'type' => join('', array_slice(explode('\\', get_class($exception)), -1)),
'code' => $exception->getCode()
];

if ($exception instanceof Exception\InvalidParameterExceptionInterface) {
$error['parameter'] = $exception->getParameter();
}

if ($this->app['debug']) {
$error['exception'] = FlattenException::create($exception)->toArray();
}

return $error;
}

/**
*
* @param GetResponseForExceptionEvent $event
Expand All @@ -67,32 +92,30 @@ public function onKernelException(GetResponseForExceptionEvent $event)

$exception = $event->getException();

$e = FlattenException::create($exception);

if ($exception instanceof HttpExceptionInterface) {
$headers = $exception->getHeaders();
$code = $exception->getStatusCode();
} else {
$code = $exception->getCode();
$code = 500;
}

if ($code < 100 || $code >= 600) {
$code = 500;
}

$error = [
'error' => [
'message' => $exception->getMessage(),
'type' => join('', array_slice(explode('\\', get_class($exception)), -1)),
'code' => $code
]
$response = [
'errors' => []
];

if ($this->app['debug']) {
$error['error']['exception'] = $e->toArray();
if ($exception instanceof Exception\ErrorCollectionException) {
foreach ($exception as $ex) {
$response['errors'][] = $this->processException($ex);
}
} else {
$response['errors'][] = $this->processException($exception);
}

$event->setResponse($this->app->json($error, $code, $headers));
$event->setResponse($this->app->json($response, $code, $headers));
}

/**
Expand Down
6 changes: 3 additions & 3 deletions tests/Application/RestTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ public function testFieldsParameterWithError()

$json = json_decode($response->getContent(), true);

$this->assertEquals('No route found for "GET /me1"', $json['error']['message']);
$this->assertEquals('NotFoundHttpException', $json['error']['type']);
$this->assertEquals(404, $json['error']['code']);
$this->assertEquals('No route found for "GET /me1"', $json['errors'][0]['message']);
$this->assertEquals('NotFoundHttpException', $json['errors'][0]['type']);
$this->assertEquals(0, $json['errors'][0]['code']);
}

public function testPrettyPrintParameter()
Expand Down
57 changes: 57 additions & 0 deletions tests/Provider/Rest/Exception/ErrorCollectionExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
/*
* This file is part of the RestServiceProvider.
*
* (c) Axel Etcheverry <axel@etcheverry.biz>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Euskadi31\Silex\Provider\Rest\Exception;

use Euskadi31\Silex\Provider\Rest\Exception\ErrorCollectionException;

class ErrorCollectionExceptionTest extends \PHPUnit_Framework_TestCase
{
public function testErrorCollectionException()
{
$errors = [];
$errors[] = $ex1 = $this->getMock('\Exception');
$errors[] = $ex2 = $this->getMock('\Exception');

$collection = new ErrorCollectionException($errors);

$this->assertEquals(400, $collection->getStatusCode());

$this->assertEquals(2, count($collection));

$collection[] = $ex3 = $this->getMock('\Exception');

$this->assertEquals(3, count($collection));

$this->assertEquals($ex3, $collection[2]);

$this->assertTrue(isset($collection[2]));

$this->assertEquals($ex1, $collection->current());
$this->assertEquals(0, $collection->key());
$this->assertTrue($collection->valid());

$collection->next();

$this->assertEquals($ex2, $collection->current());
$this->assertEquals(1, $collection->key());
$this->assertTrue($collection->valid());

$collection->rewind();

$this->assertEquals($ex1, $collection->current());
$this->assertEquals(0, $collection->key());
$this->assertTrue($collection->valid());

unset($collection[0]);

$this->assertFalse(isset($collection[0]));
}
}
64 changes: 62 additions & 2 deletions tests/Provider/Rest/RestListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
namespace Euskadi31\Silex\Provider\Rest;

use Euskadi31\Silex\Provider\Rest\RestListener;
use Euskadi31\Silex\Provider\Rest\Exception\ErrorCollectionException;
use Euskadi31\Silex\Provider\Rest\Exception\InvalidParameterExceptionInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\Request;
use Silex\Application;
Expand Down Expand Up @@ -77,8 +79,8 @@ public function testKernelExceptionWithHeader()
]);

$getResponseForExceptionEventMock = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
->disableOriginalConstructor()
->getMock();
->disableOriginalConstructor()
->getMock();
$getResponseForExceptionEventMock->expects($this->once())
->method('getException')
->will($this->returnValue($exceptionMock));
Expand All @@ -92,4 +94,62 @@ public function testKernelExceptionWithHeader()

$listener->onKernelException($getResponseForExceptionEventMock);
}

public function testKernelExceptionWithErrorCollectionException()
{
$that = $this;

$appMock = new Application;

$collection = new ErrorCollectionException(
[
new ExceptionDemo('foo', 10400),
new ExceptionDemo('foo2', 10800)
],
4,
[
'WWW-Authenticate' => 'Bearer foo'
]
);

$getResponseForExceptionEventMock = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
->disableOriginalConstructor()
->getMock();
$getResponseForExceptionEventMock->expects($this->once())
->method('getException')
->will($this->returnValue($collection));
$getResponseForExceptionEventMock->expects($this->once())
->method('setResponse')
->will($this->returnCallback(function($response) use ($that) {
$that->assertEquals('Bearer foo', $response->headers->get('WWW-Authenticate'));
$that->assertEquals(json_encode([
'errors' => [
[
'message' => 'foo',
'type' => 'ExceptionDemo',
'code' => 10400,
'parameter' => 'username'
],
[
'message' => 'foo2',
'type' => 'ExceptionDemo',
'code' => 10800,
'parameter' => 'username'
],
]
]), $response->getContent());
}));

$listener = new RestListener($appMock);

$listener->onKernelException($getResponseForExceptionEventMock);
}
}

class ExceptionDemo extends \Exception implements InvalidParameterExceptionInterface
{
public function getParameter()
{
return 'username';
}
}
Loading

0 comments on commit 4ed20a6

Please sign in to comment.