Skip to content

Commit

Permalink
Refactor to use Hal and vnd.error repsonse objects
Browse files Browse the repository at this point in the history
  • Loading branch information
jsor committed Oct 11, 2014
1 parent 30e0949 commit dcd5c82
Show file tree
Hide file tree
Showing 22 changed files with 595 additions and 203 deletions.
15 changes: 15 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,26 @@
"symfony/event-dispatcher": "~2.1",
"nocarrier/hal": "~0.9"
},
"suggest": {
"willdurand/negotiation": "~1.3",
"symfony/form": "~2.1",
"symfony/validator": "~2.1"
},
"require-dev": {
"willdurand/negotiation": "~1.3",
"symfony/form": "~2.1",
"symfony/validator": "~2.1"
},
"autoload": {
"psr-4": {
"Jsor\\Stack\\Hal\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Jsor\\Stack\\Hal\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
Expand Down
14 changes: 5 additions & 9 deletions src/EventListener/ResponseConversionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace Jsor\Stack\Hal\EventListener;

use Jsor\Stack\Hal\ResponseConverter;
use Jsor\Stack\Hal\Response\HalResponse;
use Nocarrier\Hal;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\KernelEvents;

Expand All @@ -19,14 +19,10 @@ public function __construct($prettyPrint = false)

public function onKernelView(GetResponseForControllerResultEvent $event)
{
$response = ResponseConverter::convert(
$event->getControllerResult(),
$event->getRequest(),
$this->prettyPrint
);
$hal = $event->getControllerResult();

if ($response instanceof Response) {
$event->setResponse($response);
if ($hal instanceof Hal) {
$event->setResponse(new HalResponse($hal, 200, [], $this->prettyPrint));
}
}

Expand Down
63 changes: 63 additions & 0 deletions src/Exception/FormErrorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Jsor\Stack\Hal\Exception;

use Nocarrier\Hal;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class FormErrorException extends BadRequestHttpException implements HalException
{
private $form;
private $logref;

public function __construct(FormInterface $form,
$message = null,
$logref = null,
\Exception $previous = null,
$code = 0)
{
parent::__construct($message, $previous, $code);

$this->form = $form;
$this->logref = $logref;
}

public function getHal()
{
$data = array(
'message' => $this->getMessage()
);

if ($this->logref) {
$data['@logref'] = $this->logref;
}

$hal = new Hal(null, $data);

$this->appendErrors($hal, $this->form, '');

return $hal;
}

private function appendErrors(Hal $hal, FormInterface $form, $path)
{
foreach ($form->getErrors() as $key => $error) {
$data = array(
'message' => $error->getMessage()
);

$path = rtrim($path, '/');

if ($path) {
$data['path'] = $path;
}

$hal->addResource('errors', new Hal(null, $data));
}

foreach ($form->all() as $child) {
$this->appendErrors($hal, $child, $path . '/' . $child->getName());
}
}
}
13 changes: 13 additions & 0 deletions src/Exception/HalException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Jsor\Stack\Hal\Exception;

use Nocarrier\Hal;

interface HalException
{
/**
* @return Hal
*/
public function getHal();
}
52 changes: 52 additions & 0 deletions src/Exception/ValidationErrorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Jsor\Stack\Hal\Exception;

use Nocarrier\Hal;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Validator\ConstraintViolationList;

class ValidationErrorException extends BadRequestHttpException implements HalException
{
private $violationList;
private $logref;

public function __construct(ConstraintViolationList $violationList,
$message = null,
$logref = null,
\Exception $previous = null,
$code = 0)
{
parent::__construct($message, $previous, $code);

$this->violationList = $violationList;
$this->logref = $logref;
}

public function getHal()
{
$data = array(
'message' => $this->getMessage()
);

if ($this->logref) {
$data['@logref'] = $this->logref;
}

$hal = new Hal(null, $data);

foreach ($this->violationList as $violation){
$path = str_replace('][', '/', $violation->getPropertyPath());
$path = '/' . trim($path, '[]');

$data = array(
'message' => $violation->getMessage(),
'path' => $path
);

$hal->addResource('errors', new Hal(null, $data));
}

return $hal;
}
}
67 changes: 6 additions & 61 deletions src/ExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

namespace Jsor\Stack\Hal;

use Jsor\Stack\Hal\Configurator\ConfiguratorInterface;
use Nocarrier\Hal;
use Jsor\Stack\Hal\Response\VndErrorResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
Expand All @@ -16,80 +13,28 @@
class ExceptionConverter implements HttpKernelInterface
{
private $app;
private $factory;
private $prettyPrint;
private $passThroughCatch;

public function __construct(HttpKernelInterface $app,
callable $factory = null,
$prettyPrint = false,
ConfiguratorInterface $configurator = null)
$passThroughCatch = false)
{
if (!$factory) {
$factory = function ($message, $statusCode) {
return new Hal(null, ['message' => $message]);
};
}
$this->app = $app;
$this->factory = $factory;
$this->prettyPrint = (bool) $prettyPrint;
$this->passThroughCatch = (bool) $passThroughCatch;
}

public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
try {
return $this->app->handle($request, $type, false);
return $this->app->handle($request, $type, $this->passThroughCatch ? $catch : false);
} catch (\Exception $exception) {
if (!$catch) {
throw $exception;
}

$response = static::convert($exception, $request, $this->factory, $this->prettyPrint);

if ($response instanceof \Exception) {
throw $response;
}

return $response;
}
}

public static function convert(\Exception $exception, Request $request, callable $factory = null, $prettyPrint = false)
{
$format = $request->attributes->get('_format');

if (!in_array($format, ['json', 'xml'])) {
return $exception;
}

$statusCode = 500;
$message = '';
$headers = [];

if ($exception instanceof HttpExceptionInterface) {
$statusCode = $exception->getStatusCode();
$message = $exception->getMessage();
$headers = $exception->getHeaders();
}

if (!$message && isset(Response::$statusTexts[$statusCode])) {
$message = Response::$statusTexts[$statusCode];
}

/* @var $hal Hal */
$hal = call_user_func($factory, $message, $statusCode, $headers);

switch ($format) {
case 'xml':
return new Response($hal->asXml($prettyPrint), $statusCode, array_merge(
$headers,
['Content-Type' => 'application/vnd.error+xml']
));

default:
return new Response($hal->asJson($prettyPrint), $statusCode, array_merge(
$headers,
['Content-Type' => 'application/vnd.error+json']
));
return VndErrorResponse::fromException($exception, $this->prettyPrint);
}
}
}
70 changes: 70 additions & 0 deletions src/Response/HalResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Jsor\Stack\Hal\Response;

use Nocarrier\Hal;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class HalResponse extends Response
{
protected $hal;
protected $requestFormat;
protected $prettyPrint;

public function __construct(Hal $hal, $status = 200, $headers = array(), $prettyPrint = false)
{
parent::__construct(null, $status, $headers);

$this->hal = $hal;
$this->prettyPrint = (bool) $prettyPrint;

$this->requestFormat = 'json';
$this->headers->set('Content-Type', 'application/hal+json');
}

public static function create($hal = null, $status = 200, $headers = array(), $prettyPrint = false)
{
return new static($hal, $status, $headers, $prettyPrint);
}

public function prepare(Request $request)
{
if ('xml' === $request->getRequestFormat()) {
$this->requestFormat = 'xml';
$this->headers->set('Content-Type', 'application/hal+xml');
}

return parent::prepare($request);
}

public function sendContent()
{
echo $this->getContent();

return $this;
}

public function setContent($content)
{
if (null !== $content && !$content instanceof Hal) {
throw new \LogicException('The content must be a Hal instance.');
}

$this->hal = $content;
}

public function getContent()
{
if (null === $this->hal) {
return null;
}

switch ($this->requestFormat) {
case 'xml':
return $this->hal->asXml($this->prettyPrint);
default:
return $this->hal->asJson($this->prettyPrint);
}
}
}
Loading

0 comments on commit dcd5c82

Please sign in to comment.