Skip to content

Commit

Permalink
Add handler for Client Request/Response Exceptions
Browse files Browse the repository at this point in the history
Fixes #14
  • Loading branch information
LauLaman committed Nov 16, 2017
1 parent ae0addd commit 9131b26
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 27 deletions.
12 changes: 12 additions & 0 deletions src/Client/Exception/ClientException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception;

use Exception;
use LauLamanApps\IzettleApi\Exception\IzettleApiException;

class ClientException extends Exception implements IzettleApiException
{
}
73 changes: 73 additions & 0 deletions src/Client/Exception/GuzzleClientExceptionHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception;

use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use LauLamanApps\IzettleApi\Client\Exception\ClientException as IzettleClientException;
use LauLamanApps\IzettleApi\Client\Exception\InvalidClient\InvalidClientIdException;
use LauLamanApps\IzettleApi\Client\Exception\InvalidGrant\InvalidUsernameOrPasswordException;
use LauLamanApps\IzettleApi\Client\Exception\InvalidGrant\TooManyFailedAttemptsException;

final class GuzzleClientExceptionHandler
{
public static function handleClientException(ClientException $exception): void
{
switch ($exception->getCode()) {
case 400:
self::handleClient400Exception($exception);
}

throw new IzettleClientException($exception->getMessage());
}

public static function handleRequestException(RequestException $exception): void
{
$responseData = json_decode($exception->getResponse()->getBody()->getContents(), true);
switch ($exception->getCode()) {
case 404:
throw new NotFoundException($responseData['developerMessage']);
}

throw new IzettleClientException($exception->getMessage());
}

private static function handleClient400Exception(ClientException $exception): void
{
$responseData = json_decode($exception->getResponse()->getBody()->getContents(), true);
switch ($responseData['error']) {
case 'invalid_grant':
self::handleInvalidGrantException($responseData);
case 'invalid_client':
self::handleInvalidClientException($responseData);
case 'unauthorized_client':
throw new InvalidClientException($responseData['error_description']);
default:
throw new InvalidClientException($responseData['error_description']);
}
}

private static function handleInvalidGrantException(array $responseData): void
{
switch ($responseData['error_description']) {
case 'INCORRECT_PASSWORD_OR_USERNAME':
throw new InvalidUsernameOrPasswordException();
case 'TOO_MANY_FAILED_ATTEMPTS':
throw new TooManyFailedAttemptsException();
default:
throw new InvalidGrantException($responseData['error_description']);
}
}

private static function handleInvalidClientException(array $responseData): void
{
switch ($responseData['error_description']) {
case 'Invalid client_id':
throw new InvalidClientIdException();
default:
throw new InvalidClientException($responseData['error_description']);
}
}
}
11 changes: 11 additions & 0 deletions src/Client/Exception/InvalidClient/InvalidClientIdException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception\InvalidClient;

use LauLamanApps\IzettleApi\Client\Exception\InvalidClientException;

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

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception;

class InvalidClientException extends ClientException
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception\InvalidGrant;

use LauLamanApps\IzettleApi\Client\Exception\InvalidGrantException;

final class InvalidUsernameOrPasswordException extends InvalidGrantException
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception\InvalidGrant;

use LauLamanApps\IzettleApi\Client\Exception\InvalidGrantException;

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

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception;

class InvalidGrantException extends ClientException
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Exception;
namespace LauLamanApps\IzettleApi\Client\Exception;

use Exception;
use LauLamanApps\IzettleApi\Exception\IzettleApiException;

class NotFoundException extends Exception implements IzettleApiException
{
Expand Down
11 changes: 0 additions & 11 deletions src/Client/Exception/PurchaseNotFoundException.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Exception;
namespace LauLamanApps\IzettleApi\Client\Purchase\Exception;

use Exception;
use LauLamanApps\IzettleApi\Exception\IzettleApiException;
Expand Down
11 changes: 11 additions & 0 deletions src/Client/Purchase/Exception/PurchaseNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Client\Purchase\Exception;

use LauLamanApps\IzettleApi\Client\Exception\NotFoundException;

final class PurchaseNotFoundException extends NotFoundException
{
}
2 changes: 1 addition & 1 deletion src/Client/Purchase/PaymentBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use LauLamanApps\IzettleApi\API\Purchase\Payment\MobilePayment;
use LauLamanApps\IzettleApi\API\Purchase\Payment\SwishPayment;
use LauLamanApps\IzettleApi\API\Purchase\Payment\VippsPayment;
use LauLamanApps\IzettleApi\Client\Exception\PaymentTypeNotConfiguredException;
use LauLamanApps\IzettleApi\Client\Purchase\Exception\PaymentTypeNotConfiguredException;
use Money\Currency;
use Money\Money;
use Ramsey\Uuid\Uuid;
Expand Down
4 changes: 2 additions & 2 deletions src/Client/PurchaseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

use LauLamanApps\IzettleApi\API\Purchase\Purchase;
use LauLamanApps\IzettleApi\API\Purchase\PurchaseHistory;
use LauLamanApps\IzettleApi\Client\Exception\PurchaseNotFoundException;
use LauLamanApps\IzettleApi\Client\Exception\NotFoundException;
use LauLamanApps\IzettleApi\Client\Purchase\Exception\PurchaseNotFoundException;
use LauLamanApps\IzettleApi\Client\Purchase\PurchaseBuilderInterface;
use LauLamanApps\IzettleApi\Client\Purchase\PurchaseHistoryBuilderInterface;
use LauLamanApps\IzettleApi\Exception\NotFoundException;
use LauLamanApps\IzettleApi\IzettleClientInterface;
use Ramsey\Uuid\UuidInterface;

Expand Down
12 changes: 9 additions & 3 deletions src/GuzzleIzettleClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
use DateTime;
use DateTimeImmutable;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use LauLamanApps\IzettleApi\API\Universal\IzettlePostable;
use LauLamanApps\IzettleApi\Client\AccessToken;
use LauLamanApps\IzettleApi\Client\ApiScope;
use LauLamanApps\IzettleApi\Client\Exception\AccessTokenExpiredException;
use LauLamanApps\IzettleApi\Client\Exception\GuzzleClientExceptionHandler;
use LauLamanApps\IzettleApi\Exception\NotFoundException;
use Psr\Http\Message\ResponseInterface;

Expand Down Expand Up @@ -74,7 +76,11 @@ public function getAccessTokenFromUserLogin(string $username, string $password):
],
];

$this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REQUEST_URL, $options));
try {
$this->setAccessToken($this->requestAccessToken(self::API_ACCESS_TOKEN_REQUEST_URL, $options));
} catch (ClientException $exception){
GuzzleClientExceptionHandler::handleClientException($exception);
}

return $this->accessToken;
}
Expand Down Expand Up @@ -102,8 +108,8 @@ public function get(string $url, ?array $queryParameters = null): ResponseInterf

try {
$response = $this->guzzleClient->get($url, $options);
} catch (RequestException $e) {
throw new NotFoundException($e->getMessage());
} catch (RequestException $exception) {
GuzzleClientExceptionHandler::handleRequestException($exception);
}

return $response;
Expand Down
11 changes: 4 additions & 7 deletions tests/Integration/Client/PurchaseClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,18 @@ public function getPurchase(): void

/**
* @test
* @expectedException \LauLamanApps\IzettleApi\Client\Exception\PurchaseNotFoundException
* @expectedException \LauLamanApps\IzettleApi\Client\Purchase\Exception\PurchaseNotFoundException
*/
public function getPurchase_404ShouldThrowException(): void
{
$iZettleClient = $this->getGuzzleIzettleClient(404, '');
$iZettleClient = $this->getGuzzleIzettleClient(404, '{"developerMessage":"XXX not found","errorType":null,"violations":[]}');
$purchaseClient = IzettleClientFactory::getPurchaseClient($iZettleClient);

$purchaseHistory = $purchaseClient->getPurchase(Uuid::uuid1());

self::assertInstanceOf(Purchase::class, $purchaseHistory);
$purchaseClient->getPurchase(Uuid::uuid1());
}

private function assertPurchase($purchase, $data): void
private function assertPurchase(Purchase $purchase, $data): void
{
self::assertInstanceOf(Purchase::class, $purchase);
self::assertSame($data['purchaseUUID'], $purchase->getUuid());
self::assertSame($data['purchaseUUID1'], (string) $purchase->getUuid1());
self::assertSame($data["amount"], (int) $purchase->getAmount()->getAmount());
Expand Down
110 changes: 110 additions & 0 deletions tests/Unit/Client/Exception/GuzzleClientExceptionHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

declare(strict_types=1);

namespace LauLamanApps\IzettleApi\Tests\Unit\Client\Exception;

use GuzzleHttp\Exception\ClientException as GuzzleClientException;
use GuzzleHttp\Exception\RequestException as GuzzleRequestException;
use LauLamanApps\IzettleApi\Client\Exception\ClientException;
use LauLamanApps\IzettleApi\Client\Exception\GuzzleClientExceptionHandler;
use LauLamanApps\IzettleApi\Client\Exception\InvalidClientException;
use LauLamanApps\IzettleApi\Client\Exception\InvalidClient\InvalidClientIdException;
use LauLamanApps\IzettleApi\Client\Exception\InvalidGrant\InvalidUsernameOrPasswordException;
use LauLamanApps\IzettleApi\Client\Exception\InvalidGrant\TooManyFailedAttemptsException;
use LauLamanApps\IzettleApi\Client\Exception\InvalidGrantException;
use LauLamanApps\IzettleApi\Client\Exception\NotFoundException;
use LauLamanApps\IzettleApi\Tests\Unit\MockeryAssertionTrait;
use Mockery;
use Mockery\MockInterface;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* @small
*/
final class GuzzleClientExceptionHandlerTest extends TestCase
{
use MockeryAssertionTrait;

/**
* @test
*
* @dataProvider getClientExceptions
*/
public function handleClientException(GuzzleClientException $exception, string $expectedException): void
{
self::expectException($expectedException);
GuzzleClientExceptionHandler::handleClientException($exception);
}

/**
* @return GuzzleClientException[]
*/
public function getClientExceptions(): array
{
return [
'undefined' => [new GuzzleClientException(0, Mockery::mock(RequestInterface::class)), ClientException::class],
'INCORRECT_PASSWORD_OR_USERNAME' => [$this->getClientException(400, 'invalid_grant', 'INCORRECT_PASSWORD_OR_USERNAME'), InvalidUsernameOrPasswordException::class],
'TOO_MANY_FAILED_ATTEMPTS' => [$this->getClientException(400, 'invalid_grant', 'TOO_MANY_FAILED_ATTEMPTS'), TooManyFailedAttemptsException::class],
'InvalidGrantException' => [$this->getClientException(400, 'invalid_grant', '[fallback]'), InvalidGrantException::class],
'Invalid client_id' => [$this->getClientException(400, 'invalid_client', 'Invalid client_id'), InvalidClientIdException::class],
'Invalid client' => [$this->getClientException(400, 'invalid_client', '[fallback]'), InvalidClientException::class],
'unauthorized_client' => [$this->getClientException(400, 'unauthorized_client', '[does not mather]'), InvalidClientException::class],
'InvalidClientException' => [$this->getClientException(400, 'fallback', '[does not mather]'), InvalidClientException::class],

];
}

/**
* @test
* @dataProvider getRequestExceptions
*/
public function handleRequestException(GuzzleRequestException $exception, string $expectedException): void
{
self::expectException($expectedException);
GuzzleClientExceptionHandler::handleRequestException($exception);
}

/**
* @return GuzzleRequestException[]
*/
public function getRequestExceptions(): array
{
return [
'undefined' => [$this->getRequestException(0, '[fallback]'), ClientException::class],
'not found' => [$this->getRequestException(404, 'not found'), NotFoundException::class],
];
}

private function getClientException(int $code ,string $error, string $errorDescription): GuzzleClientException
{
/** @var RequestInterface|MockInterface $request */
$request = Mockery::mock(RequestInterface::class);
$response = $this->getResponse($code, ['error'=> $error, 'error_description' => $errorDescription]);

return new GuzzleClientException('', $request, $response);
}

private function getRequestException(int $code, string $developerMessage): GuzzleRequestException
{
/** @var RequestInterface|MockInterface $request */
$request = Mockery::mock(RequestInterface::class);
$response = $this->getResponse($code, ['developerMessage' => $developerMessage]);

return new GuzzleRequestException('', $request, $response);
}

private function getResponse(int $code, array $returnData): ResponseInterface
{
/** @var ResponseInterface|MockInterface $response */
$response = Mockery::mock(ResponseInterface::class);
$response->shouldReceive('getStatusCode')->once()->andReturn($code);
$response->shouldReceive('getBody')->once()->andReturnSelf();
$response->shouldReceive('getContents')->once()->andReturn(json_encode($returnData));

return $response;
}

}
2 changes: 1 addition & 1 deletion tests/Unit/Client/Purchase/PaymentBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public function getPaymentData(): array

/**
* @test
* @expectedException \LauLamanApps\IzettleApi\Client\Exception\PaymentTypeNotConfiguredException
* @expectedException \LauLamanApps\IzettleApi\Client\Purchase\Exception\PaymentTypeNotConfiguredException
*/
public function parseNonConfiguredPaymentType(): void
{
Expand Down

0 comments on commit 9131b26

Please sign in to comment.