Skip to content

Commit c43cb5f

Browse files
authored
Merge pull request #28 from Art4/add-endpoint-chargebytbnr
Implement get one charge by tbnr
2 parents d1dbdc9 + cf61bbe commit c43cb5f

File tree

4 files changed

+302
-2
lines changed

4 files changed

+302
-2
lines changed

CHANGELOG.md

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

1010
### Added
1111

12+
- New method `Art4\Wegliphant\Client::getChargeByTbnr()` to get one charge by tbnr.
1213
- The supported weg.li-API spec as Swagger 2.0 json format was added.
1314

1415
### Changed

README.md

+31-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ $client = \Art4\Wegliphant\Client::create(
4141
);
4242
```
4343

44-
Optionally, you can use `authenticate()` to set an API key. Some areas of the weg.li API require an API key.
45-
Without the API key, all requests are sent without authorization.
44+
Optionally, you can use `authenticate()` to set an API key. Nearly all areas of the weg.li API require an API key.
45+
Without the API key, all requests are send without authorization.
4646
You can find your API key [here](https://www.weg.li/user/edit).
4747

4848
```php
@@ -252,6 +252,35 @@ $charges = $client->listCharges();
252252
],
253253
```
254254

255+
### List one charge by tbnr
256+
257+
```php
258+
$charge = $client->getChargeByTbnr('141313');
259+
260+
// $charge contains:
261+
[
262+
'tbnr' => '141313',
263+
'description' => 'Sie parkten im absoluten Haltverbot (Zeichen 283) und behinderten +) dadurch Andere.',
264+
'fine' => '40.0',
265+
'bkat' => '§ 41 Abs. 1 iVm Anlage 2, § 1 Abs. 2, § 49 StVO; § 24 Abs. 1, 3 Nr. 5 StVG; 52.1 BKat; § 19 OWiG',
266+
'penalty' => null,
267+
'fap' => null,
268+
'points' => 0,
269+
'valid_from' => '2021-11-09T00:00:00.000+01:00',
270+
'valid_to' => null,
271+
'implementation' => 2,
272+
'classification' => 5,
273+
'variant_table_id' => 741017,
274+
'rule_id' => 39,
275+
'table_id' => null,
276+
'required_refinements' => '00000000000000000000000000000000',
277+
'number_required_refinements' => 1,
278+
'max_fine' => '0.0',
279+
'created_at' => '2023-09-18T15:30:43.329+02:00',
280+
'updated_at' => '2023-09-18T15:30:43.329+02:00',
281+
],
282+
```
283+
255284
## Development
256285

257286
You can use Docker to create the full develoment environment.

src/Client.php

+17
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ public function listCharges(): array
8484
return $this->parseJsonResponseToArray($response, 200);
8585
}
8686

87+
/**
88+
* Get one charge by tbnr using the endpoint `GET /api/charges/<tbnr>`
89+
*
90+
* @link https://www.weg.li/api
91+
*
92+
* @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request.
93+
* @throws UnexpectedResponseException If an error happens while processing the response.
94+
*
95+
* @return mixed[]
96+
*/
97+
public function getChargeByTbnr(string $tbnr): array
98+
{
99+
$response = $this->sendJsonRequest('GET', '/api/charges/' . $tbnr);
100+
101+
return $this->parseJsonResponseToArray($response, 200);
102+
}
103+
87104
/**
88105
* List all notices for the authorized user using the endpoint `GET /api/notices`
89106
*
+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Art4\Wegliphant\Client;
6+
7+
use Art4\Wegliphant\Client;
8+
use Art4\Wegliphant\Exception\UnexpectedResponseException;
9+
use PHPUnit\Framework\Attributes\CoversClass;
10+
use PHPUnit\Framework\TestCase;
11+
use Psr\Http\Client\ClientExceptionInterface;
12+
use Psr\Http\Client\ClientInterface;
13+
use Psr\Http\Message\RequestFactoryInterface;
14+
use Psr\Http\Message\RequestInterface;
15+
use Psr\Http\Message\ResponseInterface;
16+
use Psr\Http\Message\StreamInterface;
17+
18+
#[CoversClass(Client::class)]
19+
final class GetChargeByTbnrTest extends TestCase
20+
{
21+
public function testGetChargeByTbnrReturnsArray(): void
22+
{
23+
$expected = [
24+
'tbnr' => '141312',
25+
'description' => 'Sie parkten im absoluten Haltverbot (Zeichen 283).',
26+
'fine' => '25.0',
27+
'bkat' => '§ 41 Abs. 1 iVm Anlage 2, § 49 StVO; § 24 Abs. 1, 3 Nr. 5 StVG; 52 BKat',
28+
'penalty' => null,
29+
'fap' => null,
30+
'points' => 0,
31+
'valid_from' => '2021-11-09T00:00:00.000+01:00',
32+
'valid_to' => null,
33+
'implementation' => null,
34+
'classification' => 5,
35+
'variant_table_id' => 741017,
36+
'rule_id' => 39,
37+
'table_id' => null,
38+
'required_refinements' => '00000000000000000000000000000000',
39+
'number_required_refinements' => 0,
40+
'max_fine' => '0.0',
41+
'created_at' => '2023-09-18T15:30:43.312+02:00',
42+
'updated_at' => '2023-09-18T15:30:43.312+02:00',
43+
];
44+
45+
$apiKey = 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2';
46+
47+
$request = $this->createMock(RequestInterface::class);
48+
$request->expects($this->exactly(2))->method('withHeader')->willReturnMap([
49+
['Accept', 'application/json', $request],
50+
['X-API-KEY', $apiKey, $request],
51+
]);
52+
53+
$requestFactory = $this->createMock(RequestFactoryInterface::class);
54+
$requestFactory->expects($this->exactly(1))->method('createRequest')->with('GET', 'https://www.weg.li/api/charges/141312')->willReturn($request);
55+
56+
$stream = $this->createConfiguredMock(
57+
StreamInterface::class,
58+
[
59+
'__toString' => json_encode($expected),
60+
],
61+
);
62+
63+
$response = $this->createConfiguredMock(
64+
ResponseInterface::class,
65+
[
66+
'getStatusCode' => 200,
67+
'getHeaderLine' => 'application/json',
68+
'getBody' => $stream,
69+
]
70+
);
71+
72+
$httpClient = $this->createMock(ClientInterface::class);
73+
$httpClient->expects($this->exactly(1))->method('sendRequest')->willReturn($response);
74+
75+
$client = Client::create(
76+
$httpClient,
77+
$requestFactory,
78+
);
79+
$client->authenticate($apiKey);
80+
81+
$response = $client->getChargeByTbnr('141312');
82+
83+
$this->assertSame(
84+
$expected,
85+
$response,
86+
);
87+
}
88+
89+
public function testGetChargeByTbnrThrowsClientException(): void
90+
{
91+
$apiKey = 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2';
92+
93+
$request = $this->createMock(RequestInterface::class);
94+
$request->expects($this->exactly(2))->method('withHeader')->willReturnMap([
95+
['Accept', 'application/json', $request],
96+
['X-API-KEY', $apiKey, $request],
97+
]);
98+
99+
$requestFactory = $this->createMock(RequestFactoryInterface::class);
100+
$requestFactory->expects($this->exactly(1))->method('createRequest')->with('GET', 'https://www.weg.li/api/charges/141312')->willReturn($request);
101+
102+
$httpClient = $this->createMock(ClientInterface::class);
103+
$httpClient->expects($this->exactly(1))->method('sendRequest')->willThrowException(
104+
$this->createMock(ClientExceptionInterface::class),
105+
);
106+
107+
$client = Client::create(
108+
$httpClient,
109+
$requestFactory,
110+
);
111+
$client->authenticate($apiKey);
112+
113+
$this->expectException(ClientExceptionInterface::class);
114+
$this->expectExceptionMessage('');
115+
116+
$client->getChargeByTbnr('141312');
117+
}
118+
119+
public function testGetChargeByTbnrThrowsUnexpectedResponseExceptionOnWrongStatusCode(): void
120+
{
121+
$request = $this->createMock(RequestInterface::class);
122+
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
123+
124+
$requestFactory = $this->createMock(RequestFactoryInterface::class);
125+
$requestFactory->expects($this->exactly(1))->method('createRequest')->with('GET', 'https://www.weg.li/api/charges/000000')->willReturn($request);
126+
127+
$response = $this->createConfiguredMock(
128+
ResponseInterface::class,
129+
[
130+
'getStatusCode' => 401,
131+
]
132+
);
133+
134+
$httpClient = $this->createMock(ClientInterface::class);
135+
$httpClient->expects($this->exactly(1))->method('sendRequest')->willReturn($response);
136+
137+
$client = Client::create(
138+
$httpClient,
139+
$requestFactory,
140+
);
141+
142+
$this->expectException(UnexpectedResponseException::class);
143+
$this->expectExceptionMessage('Server replied with the status code 401, but 200 was expected.');
144+
145+
$client->getChargeByTbnr('000000');
146+
}
147+
148+
public function testGetChargeByTbnrThrowsUnexpectedResponseExceptionOnWrongContentTypeHeader(): void
149+
{
150+
$request = $this->createMock(RequestInterface::class);
151+
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
152+
153+
$requestFactory = $this->createMock(RequestFactoryInterface::class);
154+
$requestFactory->expects($this->exactly(1))->method('createRequest')->with('GET', 'https://www.weg.li/api/charges/141312')->willReturn($request);
155+
156+
$response = $this->createConfiguredMock(
157+
ResponseInterface::class,
158+
[
159+
'getStatusCode' => 200,
160+
'getHeaderLine' => 'text/html',
161+
]
162+
);
163+
164+
$httpClient = $this->createMock(ClientInterface::class);
165+
$httpClient->expects($this->exactly(1))->method('sendRequest')->willReturn($response);
166+
167+
$client = Client::create(
168+
$httpClient,
169+
$requestFactory,
170+
);
171+
172+
$this->expectException(UnexpectedResponseException::class);
173+
$this->expectExceptionMessage('Server replied not with JSON content.');
174+
175+
$client->getChargeByTbnr('141312');
176+
}
177+
178+
public function testGetChargeByTbnrThrowsUnexpectedResponseExceptionOnInvalidJsonBody(): void
179+
{
180+
$request = $this->createMock(RequestInterface::class);
181+
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
182+
183+
$requestFactory = $this->createMock(RequestFactoryInterface::class);
184+
$requestFactory->expects($this->exactly(1))->method('createRequest')->with('GET', 'https://www.weg.li/api/charges/141312')->willReturn($request);
185+
186+
$stream = $this->createConfiguredMock(
187+
StreamInterface::class,
188+
[
189+
'__toString' => 'invalid json',
190+
],
191+
);
192+
193+
$response = $this->createConfiguredMock(
194+
ResponseInterface::class,
195+
[
196+
'getStatusCode' => 200,
197+
'getHeaderLine' => 'application/json',
198+
'getBody' => $stream,
199+
]
200+
);
201+
202+
$httpClient = $this->createMock(ClientInterface::class);
203+
$httpClient->expects($this->exactly(1))->method('sendRequest')->willReturn($response);
204+
205+
$client = Client::create(
206+
$httpClient,
207+
$requestFactory,
208+
);
209+
210+
$this->expectException(UnexpectedResponseException::class);
211+
$this->expectExceptionMessage('Response body contains no valid JSON: invalid json');
212+
213+
$client->getChargeByTbnr('141312');
214+
}
215+
216+
public function testGetChargeByTbnrThrowsUnexpectedResponseExceptionOnJsonBodyWithoutArray(): void
217+
{
218+
$request = $this->createMock(RequestInterface::class);
219+
$request->expects($this->exactly(1))->method('withHeader')->willReturn($request);
220+
221+
$requestFactory = $this->createMock(RequestFactoryInterface::class);
222+
$requestFactory->expects($this->exactly(1))->method('createRequest')->with('GET', 'https://www.weg.li/api/charges/141312')->willReturn($request);
223+
224+
$stream = $this->createConfiguredMock(
225+
StreamInterface::class,
226+
[
227+
'__toString' => '"this is not an array"',
228+
],
229+
);
230+
231+
$response = $this->createConfiguredMock(
232+
ResponseInterface::class,
233+
[
234+
'getStatusCode' => 200,
235+
'getHeaderLine' => 'application/json',
236+
'getBody' => $stream,
237+
]
238+
);
239+
240+
$httpClient = $this->createMock(ClientInterface::class);
241+
$httpClient->expects($this->exactly(1))->method('sendRequest')->willReturn($response);
242+
243+
$client = Client::create(
244+
$httpClient,
245+
$requestFactory,
246+
);
247+
248+
$this->expectException(UnexpectedResponseException::class);
249+
$this->expectExceptionMessage('Response JSON does not contain an array: "this is not an array"');
250+
251+
$client->getChargeByTbnr('141312');
252+
}
253+
}

0 commit comments

Comments
 (0)