Skip to content

Commit cd03f9e

Browse files
committed
simplified code and removed stream factory from constructor (bc break)
1 parent 08894ba commit cd03f9e

File tree

11 files changed

+174
-53
lines changed

11 files changed

+174
-53
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use Setono\DAO\Client\Client;
3333
$psr17Factory = new Psr17Factory();
3434
$httpClient = new Curl($psr17Factory);
3535

36-
$client = new Client($httpClient, $psr17Factory, $psr17Factory, 'INSERT CUSTOMER ID', 'INSERT PASSWORD');
36+
$client = new Client($httpClient, $psr17Factory, 'INSERT CUSTOMER ID', 'INSERT PASSWORD');
3737
$client->get('/DAOPakkeshop/FindPakkeshop.php', [
3838
'postnr' => '9000', // zip code
3939
'adresse' => 'Hansenvej 10', // address

composer.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414
"psr/http-client": "^1.0",
1515
"psr/http-factory": "^1.0",
1616
"psr/http-message": "^1.0",
17-
"thecodingmachine/safe": "^0.1.15"
17+
"thecodingmachine/safe": "^0.1.15",
18+
"webmozart/assert": "^1.4"
1819
},
1920
"require-dev": {
2021
"kriswallsmith/buzz": "^1.0",
2122
"localheinz/composer-normalize": "^1.1",
2223
"nyholm/psr7": "^1.1",
2324
"phpspec/phpspec": "^5.1",
24-
"phpstan/phpstan": "^0.10.3",
25+
"phpstan/phpstan": "^0.11",
26+
"phpstan/phpstan-strict-rules": "^0.11",
2527
"phpunit/phpunit": "^8.0",
2628
"symplify/easy-coding-standard": "^5.1",
2729
"thecodingmachine/phpstan-safe-rule": "^0.1.3"
@@ -31,7 +33,7 @@
3133
},
3234
"extra": {
3335
"branch-alias": {
34-
"dev-master": "1.0-dev"
36+
"dev-master": "2.1-dev"
3537
}
3638
},
3739
"autoload": {

easy-coding-standard.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ imports:
44
services:
55
PhpCsFixer\Fixer\Import\OrderedImportsFixer: ~
66
PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer: ~
7+
PhpCsFixer\Fixer\Phpdoc\NoSuperfluousPhpdocTagsFixer: ~
8+
PhpCsFixer\Fixer\ClassNotation\ClassAttributesSeparationFixer: ~

spec/Client/ClientSpec.php

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,30 @@
66

77
use PhpSpec\ObjectBehavior;
88
use Prophecy\Argument;
9+
use Psr\Http\Client\ClientExceptionInterface;
910
use Psr\Http\Client\ClientInterface as HttpClientInterface;
1011
use Psr\Http\Message\RequestFactoryInterface;
12+
use Psr\Http\Message\RequestInterface;
1113
use Psr\Http\Message\ResponseInterface;
12-
use Psr\Http\Message\StreamFactoryInterface;
1314
use Psr\Http\Message\StreamInterface;
15+
use Safe\Exceptions\JsonException;
16+
use Safe\Exceptions\StringsException;
1417
use Setono\DAO\Client\Client;
1518
use Setono\DAO\Client\ClientInterface;
19+
use Setono\DAO\Exception\ApiException;
20+
use Setono\DAO\Exception\NotOkStatusCodeException;
1621

1722
class ClientSpec extends ObjectBehavior
1823
{
1924
private const CUSTOMER_ID = '123456';
25+
2026
private const PASSWORD = 'p4ssw0rd';
27+
2128
private const BASE_URL = 'https://api.dao.as';
2229

23-
public function let(
24-
HttpClientInterface $httpClient,
25-
RequestFactoryInterface $requestFactory,
26-
StreamFactoryInterface $streamFactory
27-
): void {
28-
$this->beConstructedWith($httpClient, $requestFactory, $streamFactory, self::CUSTOMER_ID, self::PASSWORD);
30+
public function let(HttpClientInterface $httpClient, RequestFactoryInterface $requestFactory): void
31+
{
32+
$this->beConstructedWith($httpClient, $requestFactory, self::CUSTOMER_ID, self::PASSWORD);
2933
}
3034

3135
public function it_is_initializable(): void
@@ -38,6 +42,11 @@ public function it_implements_client_interface(): void
3842
$this->shouldImplement(ClientInterface::class);
3943
}
4044

45+
/**
46+
* @throws ClientExceptionInterface
47+
* @throws JsonException
48+
* @throws StringsException
49+
*/
4150
public function it_gets(
4251
HttpClientInterface $httpClient,
4352
RequestFactoryInterface $requestFactory,
@@ -60,4 +69,48 @@ public function it_gets(
6069
'items' => [1, 2, 3],
6170
]);
6271
}
72+
73+
/**
74+
* @throws ClientExceptionInterface
75+
*/
76+
public function it_throws_not_ok_status_code_exception(
77+
HttpClientInterface $httpClient,
78+
RequestFactoryInterface $requestFactory,
79+
ResponseInterface $response
80+
): void {
81+
$requestFactory
82+
->createRequest('GET', Argument::any())
83+
->shouldBeCalled();
84+
85+
$response->getStatusCode()->willReturn(500);
86+
$httpClient->sendRequest(Argument::any())->willReturn($response);
87+
88+
$this->shouldThrow(NotOkStatusCodeException::class)->during('get', ['endpoint']);
89+
}
90+
91+
/**
92+
* @throws ClientExceptionInterface
93+
* @throws StringsException
94+
*/
95+
public function it_throws_api_exception(
96+
HttpClientInterface $httpClient,
97+
RequestFactoryInterface $requestFactory,
98+
RequestInterface $request,
99+
ResponseInterface $response,
100+
StreamInterface $stream
101+
): void {
102+
$requestFactory
103+
->createRequest('GET', Argument::any())
104+
->willReturn($request)
105+
;
106+
107+
$response->getStatusCode()->willReturn(200);
108+
$response->getBody()->willReturn($stream);
109+
$stream->__toString()->willReturn('{"status":"FEJL", "fejlkode": 101, "fejltekst": "Wrong login"}');
110+
$httpClient->sendRequest($request)->willReturn($response);
111+
112+
$exception = new ApiException($request->getWrappedObject(), $response->getWrappedObject(), 200, 101, 'Wrong login');
113+
114+
$this->shouldThrow($exception)->during('get', ['endpoint']);
115+
}
63116
}

src/Client/Client.php

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44

55
namespace Setono\DAO\Client;
66

7+
use InvalidArgumentException;
78
use Psr\Http\Client\ClientExceptionInterface;
89
use Psr\Http\Client\ClientInterface as HttpClientInterface;
910
use Psr\Http\Message\RequestFactoryInterface;
10-
use Psr\Http\Message\StreamFactoryInterface;
1111
use Safe\Exceptions\JsonException;
1212
use Safe\Exceptions\StringsException;
1313
use function Safe\json_decode;
14-
use Setono\DAO\Exception\RequestFailedException;
14+
use Setono\DAO\Exception\ApiException;
15+
use Setono\DAO\Exception\NotOkStatusCodeException;
16+
use Webmozart\Assert\Assert;
1517

1618
final class Client implements ClientInterface
1719
{
@@ -25,11 +27,6 @@ final class Client implements ClientInterface
2527
*/
2628
private $requestFactory;
2729

28-
/**
29-
* @var StreamFactoryInterface
30-
*/
31-
private $streamFactory;
32-
3330
/**
3431
* @var string
3532
*/
@@ -48,47 +45,29 @@ final class Client implements ClientInterface
4845
public function __construct(
4946
HttpClientInterface $httpClient,
5047
RequestFactoryInterface $requestFactory,
51-
StreamFactoryInterface $streamFactory,
5248
string $customerId,
5349
string $password,
5450
string $baseUrl = 'https://api.dao.as'
5551
) {
5652
$this->httpClient = $httpClient;
5753
$this->requestFactory = $requestFactory;
58-
$this->streamFactory = $streamFactory;
5954
$this->customerId = $customerId;
6055
$this->password = $password;
6156
$this->baseUrl = $baseUrl;
6257
}
6358

6459
/**
65-
* @param string $endpoint
66-
* @param array $params
67-
*
68-
* @return array
60+
* {@inheritdoc}
6961
*
7062
* @throws ClientExceptionInterface
7163
* @throws JsonException
7264
* @throws StringsException
65+
* @throws InvalidArgumentException
7366
*/
7467
public function get(string $endpoint, array $params = []): array
7568
{
76-
return $this->sendRequest('GET', $endpoint, $params);
77-
}
69+
Assert::notContains($endpoint, '?', 'Do not add query parameters to the endpoint. Instead use the third argument, $params');
7870

79-
/**
80-
* @param string $method
81-
* @param string $endpoint
82-
* @param array $params
83-
*
84-
* @return array
85-
*
86-
* @throws ClientExceptionInterface
87-
* @throws JsonException
88-
* @throws StringsException
89-
*/
90-
private function sendRequest(string $method, string $endpoint, array $params = []): array
91-
{
9271
$params = array_merge([
9372
'kundeid' => $this->customerId,
9473
'kode' => $this->password,
@@ -99,18 +78,18 @@ private function sendRequest(string $method, string $endpoint, array $params = [
9978

10079
$url = $this->baseUrl.'/'.$endpoint.'?'.http_build_query($params, '', '&', PHP_QUERY_RFC3986);
10180

102-
$request = $this->requestFactory->createRequest($method, $url);
81+
$request = $this->requestFactory->createRequest('GET', $url);
10382

10483
$response = $this->httpClient->sendRequest($request);
10584

10685
if (200 !== $response->getStatusCode()) {
107-
throw new RequestFailedException($request, $response, $response->getStatusCode());
86+
throw new NotOkStatusCodeException($request, $response, $response->getStatusCode());
10887
}
10988

11089
$data = (array) json_decode((string) $response->getBody(), true);
11190

11291
if (isset($data['status']) && 'FEJL' === $data['status']) {
113-
throw new RequestFailedException($request, $response, $response->getStatusCode());
92+
throw new ApiException($request, $response, $response->getStatusCode(), $data['fejlkode'], $data['fejltekst']);
11493
}
11594

11695
return $data;

src/Client/ClientInterface.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44

55
namespace Setono\DAO\Client;
66

7+
use Setono\DAO\Exception\ApiException;
8+
use Setono\DAO\Exception\NotOkStatusCodeException;
9+
710
interface ClientInterface
811
{
912
/**
1013
* Sends a GET request to the specified endpoint with the given query params.
1114
*
12-
* @param string $endpoint
13-
* @param array $params
14-
*
15-
* @return array
15+
* @throws NotOkStatusCodeException
16+
* @throws ApiException
1617
*/
1718
public function get(string $endpoint, array $params = []): array;
1819
}

src/Exception/ApiException.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\DAO\Exception;
6+
7+
use Psr\Http\Message\RequestInterface;
8+
use Psr\Http\Message\ResponseInterface;
9+
10+
final class ApiException extends RequestException
11+
{
12+
/**
13+
* @var int|null
14+
*/
15+
private $errorCode;
16+
17+
/**
18+
* @var string|null
19+
*/
20+
private $errorMessage;
21+
22+
public function __construct(RequestInterface $request, ResponseInterface $response, int $statusCode, int $errorCode, string $errorMessage)
23+
{
24+
$this->errorCode = $errorCode;
25+
$this->errorMessage = $errorMessage;
26+
27+
parent::__construct($request, $response, $statusCode);
28+
}
29+
30+
public function getErrorCode(): ?int
31+
{
32+
return $this->errorCode;
33+
}
34+
35+
public function getErrorMessage(): ?string
36+
{
37+
return $this->errorMessage;
38+
}
39+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\DAO\Exception;
6+
7+
final class NotOkStatusCodeException extends RequestException
8+
{
9+
}

src/Exception/RequestFailedException.php renamed to src/Exception/RequestException.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,24 @@
1010
use Safe\Exceptions\StringsException;
1111
use function Safe\sprintf;
1212

13-
final class RequestFailedException extends RuntimeException
13+
abstract class RequestException extends RuntimeException
1414
{
15+
/**
16+
* @var RequestInterface
17+
*/
1518
private $request;
19+
20+
/**
21+
* @var ResponseInterface
22+
*/
1623
private $response;
24+
25+
/**
26+
* @var int
27+
*/
1728
private $statusCode;
1829

1930
/**
20-
* @param RequestInterface $request
21-
* @param ResponseInterface $response
22-
* @param int $statusCode
23-
*
2431
* @throws StringsException
2532
*/
2633
public function __construct(RequestInterface $request, ResponseInterface $response, int $statusCode)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\DAO\Exception;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Psr\Http\Message\RequestInterface;
9+
use Psr\Http\Message\ResponseInterface;
10+
11+
final class ApiExceptionTest extends TestCase
12+
{
13+
/**
14+
* @test
15+
*/
16+
public function it_returns_correct_values(): void
17+
{
18+
$request = $this->createMock(RequestInterface::class);
19+
$response = $this->createMock(ResponseInterface::class);
20+
21+
$exception = new ApiException($request, $response, 200, 101, 'error');
22+
23+
$this->assertSame($request, $exception->getRequest());
24+
$this->assertSame($response, $exception->getResponse());
25+
$this->assertSame(200, $exception->getStatusCode());
26+
$this->assertSame(101, $exception->getErrorCode());
27+
$this->assertSame('error', $exception->getErrorMessage());
28+
}
29+
}

0 commit comments

Comments
 (0)