Skip to content

Commit

Permalink
Merge pull request #18 from Calendart/imp-errors
Browse files Browse the repository at this point in the history
Improvements and handle API errors
  • Loading branch information
Taluu committed Apr 14, 2016
2 parents 7dd47ee + 72afa65 commit 6306093
Show file tree
Hide file tree
Showing 16 changed files with 290 additions and 19 deletions.
11 changes: 4 additions & 7 deletions src/Api/CalendarApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

use CalendArt\Adapter\Office365\Model\Calendar;
use CalendArt\Adapter\Office365\Office365Adapter;
use CalendArt\Adapter\Office365\Exception\ApiErrorException;

/**
* Office365 API for the Calendars
Expand All @@ -28,6 +27,8 @@
*/
class CalendarApi implements CalendarApiInterface
{
use ResponseHandler;

/** @var Guzzle Guzzle Http Client to use */
private $guzzle;

Expand All @@ -46,9 +47,7 @@ public function getList()
$request = $this->guzzle->createRequest('GET', 'calendars');
$response = $this->guzzle->send($request);

if (200 > $response->getStatusCode() || 300 <= $response->getStatusCode()) {
throw new ApiErrorException($response);
}
$this->handleResponse($response);

$result = $response->json();
$list = new ArrayCollection;
Expand All @@ -65,9 +64,7 @@ public function get($identifier)
$request = $this->guzzle->createRequest('GET', sprintf('calendars/%s', $identifier));
$response = $this->guzzle->send($request);

if (200 > $response->getStatusCode() || 300 <= $response->getStatusCode()) {
throw new ApiErrorException($response);
}
$this->handleResponse($response);

return Calendar::hydrate($response->json());
}
Expand Down
11 changes: 4 additions & 7 deletions src/Api/EventApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
use CalendArt\Adapter\Office365\Model\Calendar;

use CalendArt\Adapter\Office365\Office365Adapter;
use CalendArt\Adapter\Office365\Exception\ApiErrorException;

/**
* Office365 API for the Calendars
Expand All @@ -34,6 +33,8 @@
*/
class EventApi implements EventApiInterface
{
use ResponseHandler;

/** @var Guzzle Guzzle Http Client to use */
private $guzzle;

Expand All @@ -51,9 +52,7 @@ private function requestEvents($url, array $params = [])
$request = $this->guzzle->createRequest('GET', $url, $params);
$response = $this->guzzle->send($request);

if (200 > $response->getStatusCode() || 300 <= $response->getStatusCode()) {
throw new ApiErrorException($response);
}
$this->handleResponse($response);

$result = $response->json();
$list = new ArrayCollection;
Expand Down Expand Up @@ -149,9 +148,7 @@ public function get($identifier)
$request = $this->guzzle->createRequest('GET', sprintf('events/%s', $identifier), []);
$response = $this->guzzle->send($request);

if (200 > $response->getStatusCode() || 300 <= $response->getStatusCode()) {
throw new ApiErrorException($response);
}
$this->handleResponse($response);

return Event::hydrate($response->json());
}
Expand Down
76 changes: 76 additions & 0 deletions src/Api/ResponseHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace CalendArt\Adapter\Office365\Api;

use GuzzleHttp\Message\ResponseInterface;

use CalendArt\Adapter\Office365\Exception;

trait ResponseHandler
{
/**
* @param ResponseInterface $response
*
* @throws Exception\BadRequestException
* @throws Exception\UnauthorizedException
* @throws Exception\ForbiddenException
* @throws Exception\NotFoundException
* @throws Exception\MethodNotAllowedException
* @throws Exception\ConflictException
* @throws Exception\GoneException
* @throws Exception\PreconditionException
* @throws Exception\LimitExceededException
* @throws Exception\InternalServerErrorException
*/
private function handleResponse(ResponseInterface $response)
{
$statusCode = (int) $response->getStatusCode();

if ($statusCode >= 200 && $statusCode < 400) {
return;
}

switch ($statusCode) {
case 400:
case 406:
case 411:
case 413:
case 415:
case 416:
case 422:
throw new Exception\BadRequestException($response);

case 401:
throw new Exception\UnauthorizedException($response);

case 403:
throw new Exception\ForbiddenException($response);

case 404:
case 501:
throw new Exception\NotFoundException($response);

case 405:
throw new Exception\MethodNotAllowedException($response);

case 409:
throw new Exception\ConflictException($response);

case 410:
throw new Exception\GoneException($response);

case 412:
throw new Exception\PreconditionException($response);

case 429:
case 507:
case 509:
throw new Exception\LimitExceededException($response);

case 500:
case 503:
default:
throw new Exception\InternalServerErrorException($response);
}
}
}
13 changes: 8 additions & 5 deletions src/Exception/ApiErrorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,25 @@

use ErrorException;

use GuzzleHttp\Message\Response;
use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\Exception\ParseException;

use CalendArt\Exception\ApiErrorInterface;

/**
* Whenever the Api returns an unexpected result
*
* @author Baptiste Clavié <[email protected]>
*/
class ApiErrorException extends ErrorException
abstract class ApiErrorException extends ErrorException implements ApiErrorInterface
{
public function __construct(Response $response)
private $details;

public function __construct(ResponseInterface $response)
{
try {
$this->details = $response->json();
$message = $this->details['error']['message'];
$message = isset($this->details['error']['message']) ? $this->details['error']['message'] : $response->getReasonPhrase();
} catch (ParseException $e) {
$message = $response->getReasonPhrase();
}
Expand All @@ -40,4 +44,3 @@ public function getDetails()
return $this->details;
}
}

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

namespace CalendArt\Adapter\Office365\Exception;

class BadRequestException extends ApiErrorException
{
}
7 changes: 7 additions & 0 deletions src/Exception/ConflictException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

class ConflictException extends ApiErrorException
{
}
7 changes: 7 additions & 0 deletions src/Exception/ForbiddenException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

class ForbiddenException extends ApiErrorException
{
}
7 changes: 7 additions & 0 deletions src/Exception/GoneException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

class GoneException extends ApiErrorException
{
}
7 changes: 7 additions & 0 deletions src/Exception/InternalServerErrorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

class InternalServerErrorException extends ApiErrorException
{
}
7 changes: 7 additions & 0 deletions src/Exception/LimitExceededException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

class LimitExceededException extends ApiErrorException
{
}
7 changes: 7 additions & 0 deletions src/Exception/MethodNotAllowedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

class MethodNotAllowedException extends ApiErrorException
{
}
9 changes: 9 additions & 0 deletions src/Exception/NotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

use CalendArt\Exception\NotFoundInterface;

class NotFoundException extends ApiErrorException implements NotFoundInterface
{
}
7 changes: 7 additions & 0 deletions src/Exception/PreconditionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

class PreconditionException extends ApiErrorException
{
}
9 changes: 9 additions & 0 deletions src/Exception/UnauthorizedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace CalendArt\Adapter\Office365\Exception;

use CalendArt\Exception\InvalidCredentialsInterface;

class UnauthorizedException extends ApiErrorException implements InvalidCredentialsInterface
{
}
77 changes: 77 additions & 0 deletions test/Api/ResponseHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace CalendArt\Adapter\Office365\Api;

use GuzzleHttp\Message\ResponseInterface;

class ResponseHandlerTest extends \PHPUnit_Framework_TestCase
{
private $response;
private $api;

protected function setUp()
{
$this->response = $this->prophesize('GuzzleHttp\Message\ResponseInterface');
$this->api = new Api;
}

public function testHandleErrorsWithSuccessfulResponse()
{
$this->response->getStatusCode()->shouldBeCalled()->willReturn(200);
$this->api->get($this->response->reveal());

$this->response->getStatusCode()->shouldBeCalled()->willReturn(301);
$this->api->get($this->response->reveal());
}

/**
* @dataProvider getResponses
*/
public function testHandleErrors($statusCode, $exception)
{
$this->setExpectedException($exception);

$this->response->getStatusCode()->shouldBeCalled()->willReturn($statusCode);
$this->response->json()->shouldBeCalled()->willReturn(['error' => ['message' => 'foo']]);
$this->api->get($this->response->reveal());
}

public function getResponses()
{
return [
[400,'CalendArt\Adapter\Office365\Exception\BadRequestException'],
[401,'CalendArt\Adapter\Office365\Exception\UnauthorizedException'],
[403,'CalendArt\Adapter\Office365\Exception\ForbiddenException'],
[404,'CalendArt\Adapter\Office365\Exception\NotFoundException'],
[405,'CalendArt\Adapter\Office365\Exception\MethodNotAllowedException'],
[406,'CalendArt\Adapter\Office365\Exception\BadRequestException'],
[409,'CalendArt\Adapter\Office365\Exception\ConflictException'],
[410,'CalendArt\Adapter\Office365\Exception\GoneException'],
[411,'CalendArt\Adapter\Office365\Exception\BadRequestException'],
[412,'CalendArt\Adapter\Office365\Exception\PreconditionException'],
[413,'CalendArt\Adapter\Office365\Exception\BadRequestException'],
[415,'CalendArt\Adapter\Office365\Exception\BadRequestException'],
[416,'CalendArt\Adapter\Office365\Exception\BadRequestException'],
[422,'CalendArt\Adapter\Office365\Exception\BadRequestException'],
[429,'CalendArt\Adapter\Office365\Exception\LimitExceededException'],
[500,'CalendArt\Adapter\Office365\Exception\InternalServerErrorException'],
[501,'CalendArt\Adapter\Office365\Exception\NotFoundException'],
[503,'CalendArt\Adapter\Office365\Exception\InternalServerErrorException'],
[507,'CalendArt\Adapter\Office365\Exception\LimitExceededException'],
[509,'CalendArt\Adapter\Office365\Exception\LimitExceededException'],
];
}
}

class Api
{
use ResponseHandler;

/**
* Simulate a get method of an API
*/
public function get(ResponseInterface $response)
{
$this->handleResponse($response);
}
}
47 changes: 47 additions & 0 deletions test/Exception/ApiErrorExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace CalendArt\Adapter\Office365;

use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\Exception\ParseException;

use CalendArt\Adapter\Office365\Exception\BadRequestException;

class ApiErrorExceptionTest extends \PHPUnit_Framework_TestCase
{
public function testConstructWithParseException()
{
$response = $this->prophesize('GuzzleHttp\Message\ResponseInterface');
$response->json()->shouldBeCalled()->willThrow(new ParseException);
$response->getStatusCode()->shouldBeCalled()->willReturn(400);
$response->getReasonPhrase()->shouldBeCalled()->willReturn('Invalid Argument');

$e = new BadRequestException($response->reveal());

$this->assertEquals('The request failed and returned an invalid status code ("400") : Invalid Argument', $e->getMessage());
}

public function testConstructWithUnexceptedFormat()
{
$response = $this->prophesize('GuzzleHttp\Message\ResponseInterface');
$response->json()->shouldBeCalled()->willReturn(['error' => []]);
$response->getStatusCode()->shouldBeCalled()->willReturn(400);
$response->getReasonPhrase()->shouldBeCalled()->willReturn('Invalid Argument');

$e = new BadRequestException($response->reveal());

$this->assertEquals('The request failed and returned an invalid status code ("400") : Invalid Argument', $e->getMessage());
}

public function testConstructWithExceptedFormat()
{
$response = $this->prophesize('GuzzleHttp\Message\ResponseInterface');
$response->json()->shouldBeCalled()->willReturn(['error' => ['message' => 'Api Message']]);
$response->getStatusCode()->shouldBeCalled()->willReturn(400);
$response->getReasonPhrase()->shouldNotBeCalled();

$e = new BadRequestException($response->reveal());

$this->assertEquals('The request failed and returned an invalid status code ("400") : Api Message', $e->getMessage());
}
}

0 comments on commit 6306093

Please sign in to comment.