Skip to content

Commit

Permalink
Merge pull request #9 from tjveldhuizen/feature-httpexception
Browse files Browse the repository at this point in the history
Added Exception class implementing HttpExceptionInterface
  • Loading branch information
veewee authored Jun 12, 2020
2 parents a0fef11 + e19de04 commit 64ab168
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Api Problem Bundle

This package provides a [RFC7807](https://tools.ietf.org/html/rfc7807) Problem details exception listener for Symfony.
Internal, this package uses the models provided by `phpro/api-problem`](https://www.github.com/phpro/api-problem).
Internal, this package uses the models provided by [`phpro/api-problem`](https://www.github.com/phpro/api-problem).
When an `ApiProblemException` is triggered, this bundle will return the correct response.


Expand Down Expand Up @@ -69,6 +69,9 @@ Body:
}
```

As an alternative, use ```ApiProblemHttpException``` instead of ```ApiProblemException```, to make it possible to
[exclude the specific status code from the log](https://symfony.com/doc/current/logging/monolog_exclude_http_codes.html)

## Adding custom exception transformations

Currently, we automatically transform exceptions from following packages to an ApiProblem instance:
Expand Down
39 changes: 39 additions & 0 deletions src/Exception/ApiProblemHttpException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Phpro\ApiProblemBundle\Exception;

use Phpro\ApiProblem\ApiProblemInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;

class ApiProblemHttpException extends HttpException
{
private $apiProblem;

public function __construct(ApiProblemInterface $apiProblem)
{
$data = $apiProblem->toArray();
$message = $data['detail'] ?? ($data['title'] ?? '');
$code = (int) ($data['status'] ?? 0);

parent::__construct($code, $message);
$this->apiProblem = $apiProblem;
}

public function getApiProblem(): ApiProblemInterface
{
return $this->apiProblem;
}

public function getStatusCode()
{
return parent::getStatusCode() > 0 ? parent::getStatusCode() : Response::HTTP_BAD_REQUEST;
}

public function getHeaders()
{
return ['Content-Type' => 'application/problem+json'];
}
}
5 changes: 3 additions & 2 deletions src/Transformer/ApiProblemExceptionTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

use Phpro\ApiProblem\ApiProblemInterface;
use Phpro\ApiProblem\Exception\ApiProblemException;
use Phpro\ApiProblemBundle\Exception\ApiProblemHttpException;

class ApiProblemExceptionTransformer implements ExceptionTransformerInterface
{
/**
* @param ApiProblemException $exception
* @param ApiProblemException|ApiProblemHttpException $exception
*/
public function transform(\Throwable $exception): ApiProblemInterface
{
Expand All @@ -19,6 +20,6 @@ public function transform(\Throwable $exception): ApiProblemInterface

public function accepts(\Throwable $exception): bool
{
return $exception instanceof ApiProblemException;
return $exception instanceof ApiProblemException || $exception instanceof ApiProblemHttpException;
}
}
76 changes: 76 additions & 0 deletions test/Exception/ApiProblemHttpExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace PhproTest\ApiProblemBundle\Exception;

use Phpro\ApiProblem\Http\HttpApiProblem;
use Phpro\ApiProblemBundle\Exception\ApiProblemHttpException;
use Phpro\ApiProblemBundle\Transformer\ApiProblemExceptionTransformer;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Symfony\Component\HttpKernel\Exception\HttpException;

class ApiProblemHttpExceptionTest extends TestCase
{
/**
* @var HttpApiProblem|ObjectProphecy
*/
private $apiProblem;

protected function setUp(): void/* The :void return type declaration that should be here would cause a BC issue */
{
$this->apiProblem = $this->prophesize(HttpApiProblem::class);
$this->apiProblem->toArray()->willReturn([]);
}

/** @test */
public function it_is_accepted_by_the_ApiProblemExceptionTransformer(): void
{
$transformer = new ApiProblemExceptionTransformer();

$this->assertTrue($transformer->accepts(new ApiProblemHttpException($this->apiProblem->reveal())));
}

/** @test */
public function it_is_an_instance_of_HttpException(): void
{
$exception = new ApiProblemHttpException($this->apiProblem->reveal());

$this->assertInstanceOf(HttpException::class, $exception);
}

/** @test */
public function it_contains_an_api_problem(): void
{
$apiProblem = $this->apiProblem->reveal();

$exception = new ApiProblemHttpException($apiProblem);
$this->assertEquals($apiProblem, $exception->getApiProblem());
}

/** @test */
public function it_returns_the_correct_http_headers(): void
{
$exception = new ApiProblemHttpException($this->apiProblem->reveal());

$this->assertEquals(['Content-Type' => 'application/problem+json'], $exception->getHeaders());
}

/** @test */
public function it_returns_the_correct_default_http_statuscode(): void
{
$exception = new ApiProblemHttpException($this->apiProblem->reveal());

$this->assertEquals(400, $exception->getStatusCode());
}

/** @test */
public function it_returns_the_correct_specified_http_statuscode(): void
{
$this->apiProblem->toArray()->willReturn(['status' => 401]);
$exception = new ApiProblemHttpException($this->apiProblem->reveal());

$this->assertEquals(401, $exception->getStatusCode());
}
}

0 comments on commit 64ab168

Please sign in to comment.