Skip to content

Commit 5f891dd

Browse files
authored
Merge pull request #17 from Art4/15-improve-exceptions
Add `UnexpectedResponseException`
2 parents 9fe308f + 32b5031 commit 5f891dd

File tree

9 files changed

+150
-38
lines changed

9 files changed

+150
-38
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased](https://github.com/Art4/wegliphant/compare/0.2.0...main)
99

10+
### Added
11+
12+
- New class `Art4\Wegliphant\Exception\UnexpectedResponseException` that will be thrown if an error happens while processing the response.
13+
14+
### Changed
15+
16+
- `Art4\Wegliphant\Client` now throws `Art4\Wegliphant\Exception\UnexpectedResponseException` if an error happens while processing responses.
17+
1018
## [0.2.0 - 2024-03-06](https://github.com/Art4/wegliphant/compare/0.1.0...0.2.0)
1119

1220
### Added

phpunit.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
cacheDirectory=".phpunit.cache"
66
executionOrder="depends,defects"
77
requireCoverageMetadata="true"
8-
beStrictAboutCoverageMetadata="true"
8+
beStrictAboutCoverageMetadata="false"
99
beStrictAboutOutputDuringTests="true"
1010
failOnRisky="true"
1111
failOnWarning="true">

src/Client.php

+10-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Art4\Wegliphant;
66

7-
use Exception;
7+
use Art4\Wegliphant\Exception\UnexpectedResponseException;
88
use JsonException;
99
use Psr\Http\Client\ClientInterface;
1010
use Psr\Http\Message\RequestFactoryInterface;
@@ -27,7 +27,7 @@ public static function create(
2727
* @link https://www.weg.li/api
2828
*
2929
* @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request.
30-
* @throws \Exception If an error happens while processing the response.
30+
* @throws UnexpectedResponseException If an error happens while processing the response.
3131
*
3232
* @return mixed[]
3333
*/
@@ -46,7 +46,7 @@ public function listDistricts(): array
4646
* @link https://www.weg.li/api
4747
*
4848
* @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request.
49-
* @throws \Exception If an error happens while processing the response.
49+
* @throws UnexpectedResponseException If an error happens while processing the response.
5050
*
5151
* @return mixed[]
5252
*/
@@ -65,7 +65,7 @@ public function getDistrictByZip(string $zip): array
6565
* @link https://www.weg.li/api
6666
*
6767
* @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request.
68-
* @throws \Exception If an error happens while processing the response.
68+
* @throws UnexpectedResponseException If an error happens while processing the response.
6969
*
7070
* @return mixed[]
7171
*/
@@ -97,23 +97,23 @@ private function sendJsonRequest(
9797
}
9898

9999
/**
100-
* @throws \Exception If the response has the wrong status code or content type header.
100+
* @throws UnexpectedResponseException If the response has the wrong status code or content type header.
101101
*/
102102
private function ensureJsonResponse(
103103
ResponseInterface $response,
104104
int $expectedStatusCode,
105105
): void {
106106
if ($response->getStatusCode() !== $expectedStatusCode) {
107-
throw new Exception('Server replied with status code ' . $response->getStatusCode());
107+
throw UnexpectedResponseException::create('Server replied with status code ' . $response->getStatusCode(), $response);
108108
}
109109

110110
if (! str_starts_with($response->getHeaderLine('content-type'), 'application/json')) {
111-
throw new Exception('Server replied not with JSON content.');
111+
throw UnexpectedResponseException::create('Server replied not with JSON content.', $response);
112112
}
113113
}
114114

115115
/**
116-
* @throws \Exception If an error happens while processing the response.
116+
* @throws UnexpectedResponseException If an error happens while parsing the JSON response.
117117
*
118118
* @return mixed[]
119119
*/
@@ -124,11 +124,11 @@ private function parseJsonResponseToArray(ResponseInterface $response): array
124124
try {
125125
$data = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
126126
} catch (JsonException $th) {
127-
throw new Exception('Response body contains no valid JSON: ' . $responseBody, 0, $th);
127+
throw UnexpectedResponseException::create('Response body contains no valid JSON: ' . $responseBody, $response, $th);
128128
}
129129

130130
if (! is_array($data)) {
131-
throw new Exception('Response JSON does not contain an array: ' . $responseBody);
131+
throw UnexpectedResponseException::create('Response JSON does not contain an array: ' . $responseBody, $response);
132132
}
133133

134134
return $data;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Art4\Wegliphant\Exception;
6+
7+
use Psr\Http\Message\ResponseInterface;
8+
use RuntimeException;
9+
use Throwable;
10+
11+
final class UnexpectedResponseException extends RuntimeException
12+
{
13+
public static function create(string $message, ResponseInterface $response, ?Throwable $previous = null): self
14+
{
15+
return new self($message, $response, $previous);
16+
}
17+
18+
private ResponseInterface $response;
19+
20+
/**
21+
* @internal Use ::create() instead
22+
*/
23+
public function __construct(string $message, ResponseInterface $response, ?Throwable $previous = null)
24+
{
25+
parent::__construct($message, 0, $previous);
26+
27+
$this->response = $response;
28+
}
29+
30+
public function getResponse(): ResponseInterface
31+
{
32+
return $this->response;
33+
}
34+
}

tests/Unit/Client/GetDistrictByZipTest.php

+9-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Tests\Art4\Wegliphant\Client;
66

77
use Art4\Wegliphant\Client;
8-
use Exception;
8+
use Art4\Wegliphant\Exception\UnexpectedResponseException;
99
use PHPUnit\Framework\Attributes\CoversClass;
1010
use PHPUnit\Framework\TestCase;
1111
use Psr\Http\Client\ClientExceptionInterface;
@@ -97,7 +97,7 @@ public function testGetDistrictByZipThrowsClientException(): void
9797
$client->getDistrictByZip('12305');
9898
}
9999

100-
public function testGetDistrictByZipExceptionOnWrongStatusCode(): void
100+
public function testGetDistrictByZipThrowsUnexpectedResponseExceptionOnWrongStatusCode(): void
101101
{
102102
$request = $this->createMock(RequestInterface::class);
103103
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -120,13 +120,13 @@ public function testGetDistrictByZipExceptionOnWrongStatusCode(): void
120120
$requestFactory,
121121
);
122122

123-
$this->expectException(Exception::class);
123+
$this->expectException(UnexpectedResponseException::class);
124124
$this->expectExceptionMessage('Server replied with status code 500');
125125

126126
$client->getDistrictByZip('00000');
127127
}
128128

129-
public function testGetDistrictByZipExceptionOnWrongContentTypeHeader(): void
129+
public function testGetDistrictByZipThrowsUnexpectedResponseExceptionOnWrongContentTypeHeader(): void
130130
{
131131
$request = $this->createMock(RequestInterface::class);
132132
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -150,13 +150,13 @@ public function testGetDistrictByZipExceptionOnWrongContentTypeHeader(): void
150150
$requestFactory,
151151
);
152152

153-
$this->expectException(Exception::class);
153+
$this->expectException(UnexpectedResponseException::class);
154154
$this->expectExceptionMessage('Server replied not with JSON content.');
155155

156156
$client->getDistrictByZip('12305');
157157
}
158158

159-
public function testGetDistrictByZipExceptionOnInvalidJsonBody(): void
159+
public function testGetDistrictByZipThrowsUnexpectedResponseExceptionOnInvalidJsonBody(): void
160160
{
161161
$request = $this->createMock(RequestInterface::class);
162162
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -188,13 +188,13 @@ public function testGetDistrictByZipExceptionOnInvalidJsonBody(): void
188188
$requestFactory,
189189
);
190190

191-
$this->expectException(Exception::class);
191+
$this->expectException(UnexpectedResponseException::class);
192192
$this->expectExceptionMessage('Response body contains no valid JSON: invalid json');
193193

194194
$client->getDistrictByZip('12305');
195195
}
196196

197-
public function testGetDistrictByZipExceptionOnJsonBodyWithoutArray(): void
197+
public function testGetDistrictByZipThrowsUnexpectedResponseExceptionOnJsonBodyWithoutArray(): void
198198
{
199199
$request = $this->createMock(RequestInterface::class);
200200
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -226,7 +226,7 @@ public function testGetDistrictByZipExceptionOnJsonBodyWithoutArray(): void
226226
$requestFactory,
227227
);
228228

229-
$this->expectException(Exception::class);
229+
$this->expectException(UnexpectedResponseException::class);
230230
$this->expectExceptionMessage('Response JSON does not contain an array: "this is not an array"');
231231

232232
$client->getDistrictByZip('12305');

tests/Unit/Client/ListChargesTest.php

+9-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Tests\Art4\Wegliphant\Client;
66

77
use Art4\Wegliphant\Client;
8-
use Exception;
8+
use Art4\Wegliphant\Exception\UnexpectedResponseException;
99
use PHPUnit\Framework\Attributes\CoversClass;
1010
use PHPUnit\Framework\TestCase;
1111
use Psr\Http\Client\ClientExceptionInterface;
@@ -169,7 +169,7 @@ public function testListChargesThrowsClientException(): void
169169
$client->listCharges();
170170
}
171171

172-
public function testListChargesExceptionOnWrongStatusCode(): void
172+
public function testListChargesThrowsUnexpectedResponseExceptionOnWrongStatusCode(): void
173173
{
174174
$request = $this->createMock(RequestInterface::class);
175175
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -192,13 +192,13 @@ public function testListChargesExceptionOnWrongStatusCode(): void
192192
$requestFactory,
193193
);
194194

195-
$this->expectException(Exception::class);
195+
$this->expectException(UnexpectedResponseException::class);
196196
$this->expectExceptionMessage('Server replied with status code 500');
197197

198198
$client->listCharges();
199199
}
200200

201-
public function testListChargesExceptionOnWrongContentTypeHeader(): void
201+
public function testListChargesThrowsUnexpectedResponseExceptionOnWrongContentTypeHeader(): void
202202
{
203203
$request = $this->createMock(RequestInterface::class);
204204
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -222,13 +222,13 @@ public function testListChargesExceptionOnWrongContentTypeHeader(): void
222222
$requestFactory,
223223
);
224224

225-
$this->expectException(Exception::class);
225+
$this->expectException(UnexpectedResponseException::class);
226226
$this->expectExceptionMessage('Server replied not with JSON content.');
227227

228228
$client->listCharges();
229229
}
230230

231-
public function testListChargesExceptionOnInvalidJsonBody(): void
231+
public function testListChargesThrowsUnexpectedResponseExceptionOnInvalidJsonBody(): void
232232
{
233233
$request = $this->createMock(RequestInterface::class);
234234
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -260,13 +260,13 @@ public function testListChargesExceptionOnInvalidJsonBody(): void
260260
$requestFactory,
261261
);
262262

263-
$this->expectException(Exception::class);
263+
$this->expectException(UnexpectedResponseException::class);
264264
$this->expectExceptionMessage('Response body contains no valid JSON: invalid json');
265265

266266
$client->listCharges();
267267
}
268268

269-
public function testListChargesExceptionOnJsonBodyWithoutArray(): void
269+
public function testListChargesThrowsUnexpectedResponseExceptionOnJsonBodyWithoutArray(): void
270270
{
271271
$request = $this->createMock(RequestInterface::class);
272272
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -298,7 +298,7 @@ public function testListChargesExceptionOnJsonBodyWithoutArray(): void
298298
$requestFactory,
299299
);
300300

301-
$this->expectException(Exception::class);
301+
$this->expectException(UnexpectedResponseException::class);
302302
$this->expectExceptionMessage('Response JSON does not contain an array: "this is not an array"');
303303

304304
$client->listCharges();

tests/Unit/Client/ListDistrictsTest.php

+9-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Tests\Art4\Wegliphant\Client;
66

77
use Art4\Wegliphant\Client;
8-
use Exception;
8+
use Art4\Wegliphant\Exception\UnexpectedResponseException;
99
use PHPUnit\Framework\Attributes\CoversClass;
1010
use PHPUnit\Framework\TestCase;
1111
use Psr\Http\Client\ClientExceptionInterface;
@@ -127,7 +127,7 @@ public function testListDistrictsThrowsClientException(): void
127127
$client->listDistricts();
128128
}
129129

130-
public function testListDistrictsExceptionOnWrongStatusCode(): void
130+
public function testListDistrictsThrowsUnexpectedResponseExceptionOnWrongStatusCode(): void
131131
{
132132
$request = $this->createMock(RequestInterface::class);
133133
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -150,13 +150,13 @@ public function testListDistrictsExceptionOnWrongStatusCode(): void
150150
$requestFactory,
151151
);
152152

153-
$this->expectException(Exception::class);
153+
$this->expectException(UnexpectedResponseException::class);
154154
$this->expectExceptionMessage('Server replied with status code 500');
155155

156156
$client->listDistricts();
157157
}
158158

159-
public function testListDistrictsExceptionOnWrongContentTypeHeader(): void
159+
public function testListDistrictsThrowsUnexpectedResponseExceptionOnWrongContentTypeHeader(): void
160160
{
161161
$request = $this->createMock(RequestInterface::class);
162162
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -180,13 +180,13 @@ public function testListDistrictsExceptionOnWrongContentTypeHeader(): void
180180
$requestFactory,
181181
);
182182

183-
$this->expectException(Exception::class);
183+
$this->expectException(UnexpectedResponseException::class);
184184
$this->expectExceptionMessage('Server replied not with JSON content.');
185185

186186
$client->listDistricts();
187187
}
188188

189-
public function testListDistrictsExceptionOnInvalidJsonBody(): void
189+
public function testListDistrictsThrowsUnexpectedResponseExceptionOnInvalidJsonBody(): void
190190
{
191191
$request = $this->createMock(RequestInterface::class);
192192
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -218,13 +218,13 @@ public function testListDistrictsExceptionOnInvalidJsonBody(): void
218218
$requestFactory,
219219
);
220220

221-
$this->expectException(Exception::class);
221+
$this->expectException(UnexpectedResponseException::class);
222222
$this->expectExceptionMessage('Response body contains no valid JSON: invalid json');
223223

224224
$client->listDistricts();
225225
}
226226

227-
public function testListDistrictsExceptionOnJsonBodyWithoutArray(): void
227+
public function testListDistrictsThrowsUnexpectedResponseExceptionOnJsonBodyWithoutArray(): void
228228
{
229229
$request = $this->createMock(RequestInterface::class);
230230
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
@@ -256,7 +256,7 @@ public function testListDistrictsExceptionOnJsonBodyWithoutArray(): void
256256
$requestFactory,
257257
);
258258

259-
$this->expectException(Exception::class);
259+
$this->expectException(UnexpectedResponseException::class);
260260
$this->expectExceptionMessage('Response JSON does not contain an array: "this is not an array"');
261261

262262
$client->listDistricts();

0 commit comments

Comments
 (0)