Skip to content

Commit ab0c70e

Browse files
wundiiawugoloroden
authored
feat: implemented readEventType and add getJsonData method (#25)
Co-authored-by: awu <[email protected]> Co-authored-by: Golo Roden <[email protected]>
1 parent fb2c764 commit ab0c70e

File tree

6 files changed

+210
-22
lines changed

6 files changed

+210
-22
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,14 @@ foreach($eventTypes as $eventType) {
450450
}
451451
```
452452

453+
### Listing a Specific Event Type
454+
455+
To list a specific event type, call the `readEventType` function with the event type as an argument. The function returns the detailed event type, which includes the schema:
456+
457+
```php
458+
$eventType = $client->readEventType('io.eventsourcingdb.library.book-acquired');
459+
```
460+
453461
### Using Testcontainers
454462

455463
Import the `Container` class, call the `start` function to run a test container, get a client, run your test code, and finally call the `stop` function to stop the test container:

src/Client.php

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,15 @@ public function ping(): void
3939
));
4040
}
4141

42-
$body = $response->getStream()->getContents();
43-
$data = json_decode($body, true);
42+
try {
43+
$data = $response->getStream()->getJsonData();
44+
} catch (RuntimeException $runtimeException) {
45+
throw new RuntimeException(
46+
'Failed to ping: ' . $runtimeException->getMessage(),
47+
$runtimeException->getCode(),
48+
$runtimeException,
49+
);
50+
}
4451

4552
if (!isset($data['type']) || $data['type'] !== 'io.eventsourcingdb.api.ping-received') {
4653
throw new RuntimeException('Failed to ping');
@@ -62,8 +69,15 @@ public function verifyApiToken(): void
6269
));
6370
}
6471

65-
$body = $response->getStream()->getContents();
66-
$data = json_decode($body, true);
72+
try {
73+
$data = $response->getStream()->getJsonData();
74+
} catch (RuntimeException $runtimeException) {
75+
throw new RuntimeException(
76+
'Failed to verify API token: ' . $runtimeException->getMessage(),
77+
$runtimeException->getCode(),
78+
$runtimeException,
79+
);
80+
}
6781

6882
if (!isset($data['type']) || $data['type'] !== 'io.eventsourcingdb.api.api-token-verified') {
6983
throw new RuntimeException('Failed to verify API token');
@@ -93,18 +107,14 @@ public function writeEvents(array $events, array $preconditions = []): array
93107
));
94108
}
95109

96-
$body = $response->getStream()->getContents();
97-
if ($body === '') {
98-
return [];
99-
}
100-
101-
if (!json_validate($body)) {
102-
throw new RuntimeException('Failed to read events, after writing.');
103-
}
104-
105-
$data = json_decode($body, true);
106-
if (!is_array($data)) {
107-
throw new RuntimeException('Failed to read events, expected an array.');
110+
try {
111+
$data = $response->getStream()->getJsonData();
112+
} catch (RuntimeException $runtimeException) {
113+
throw new RuntimeException(
114+
'Failed to read events, after writing: ' . $runtimeException->getMessage(),
115+
$runtimeException->getCode(),
116+
$runtimeException,
117+
);
108118
}
109119

110120
$writtenEvents = array_map(
@@ -350,4 +360,39 @@ public function readEventTypes(): iterable
350360
}
351361
}
352362
}
363+
364+
public function readEventType(string $eventType): EventType
365+
{
366+
$response = $this->httpClient->post(
367+
'/api/v1/read-event-type',
368+
$this->apiToken,
369+
[
370+
'eventType' => $eventType,
371+
],
372+
);
373+
374+
$status = $response->getStatusCode();
375+
if ($status !== 200) {
376+
throw new RuntimeException(sprintf(
377+
"Failed to read event type, got HTTP status code '%d', expected '200'",
378+
$status
379+
));
380+
}
381+
382+
try {
383+
$data = $response->getStream()->getJsonData();
384+
} catch (RuntimeException $runtimeException) {
385+
throw new RuntimeException(
386+
'Failed to read event type: ' . $runtimeException->getMessage(),
387+
$runtimeException->getCode(),
388+
$runtimeException,
389+
);
390+
}
391+
392+
return new EventType(
393+
$data['eventType'],
394+
$data['isPhantom'],
395+
$data['schema'] ?? [],
396+
);
397+
}
353398
}

src/Stream/Stream.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Thenativeweb\Eventsourcingdb\Stream;
66

77
use IteratorAggregate;
8+
use RuntimeException;
89
use Stringable;
910
use Traversable;
1011

@@ -31,4 +32,24 @@ public function getContents(): string
3132
{
3233
return implode('', iterator_to_array($this));
3334
}
35+
36+
public function getJsonData(): array
37+
{
38+
$contents = $this->getContents();
39+
if ($contents === '') {
40+
return [];
41+
}
42+
43+
if (!json_validate($contents)) {
44+
throw new RuntimeException('invalid json string');
45+
}
46+
47+
$data = json_decode($contents, true);
48+
if (!is_array($data)) {
49+
$dataType = gettype($data);
50+
throw new RuntimeException("json data is from type '{$dataType}', expected an array");
51+
}
52+
53+
return $data;
54+
}
3455
}

tests/ReadEventTypeTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Thenativeweb\Eventsourcingdb\EventCandidate;
7+
use Thenativeweb\Eventsourcingdb\EventType;
8+
use Thenativeweb\Eventsourcingdb\Tests\ClientTestTrait;
9+
10+
final class ReadEventTypeTest extends TestCase
11+
{
12+
use ClientTestTrait;
13+
14+
public function testFailsIfTheEventTypeDoesNotExist(): void
15+
{
16+
$this->expectExceptionMessage("Failed to read event type, got HTTP status code '404', expected '200'");
17+
18+
$this->client->readEventType('non.existent.eventType');
19+
}
20+
21+
public function testFailsIfTheEventTypeIsMalformed(): void
22+
{
23+
$this->expectExceptionMessage("Failed to read event type, got HTTP status code '400', expected '200'");
24+
25+
$this->client->readEventType('malformed.eventType.');
26+
}
27+
28+
public function testReadAnExistingEventType(): void
29+
{
30+
$firstEvent = new EventCandidate(
31+
source: 'https://www.eventsourcingdb.io',
32+
subject: '/test',
33+
type: 'io.eventsourcingdb.test.foo',
34+
data: [
35+
'value' => 23,
36+
],
37+
);
38+
39+
$secondEvent = new EventCandidate(
40+
source: 'https://www.eventsourcingdb.io',
41+
subject: '/test',
42+
type: 'io.eventsourcingdb.test.bar',
43+
data: [
44+
'value' => 42,
45+
],
46+
);
47+
48+
$this->client->writeEvents([
49+
$firstEvent,
50+
$secondEvent,
51+
]);
52+
53+
$eventType = $this->client->readEventType('io.eventsourcingdb.test.foo');
54+
55+
$expected = new EventType(
56+
eventType: 'io.eventsourcingdb.test.foo',
57+
isPhantom: false,
58+
schema: [],
59+
);
60+
61+
$this->assertEquals($expected, $eventType);
62+
}
63+
}

tests/Stream/StreamTest.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use ArrayIterator;
88
use PHPUnit\Framework\TestCase;
9+
use RuntimeException;
910
use Thenativeweb\Eventsourcingdb\Stream\CurlMultiHandler;
1011
use Thenativeweb\Eventsourcingdb\Stream\Stream;
1112

@@ -44,4 +45,54 @@ public function testToStringReturnsContents(): void
4445

4546
$this->assertSame('foobar', (string) $stream);
4647
}
48+
49+
public function testThrowsExceptionOnInvalidJson(): void
50+
{
51+
$this->expectException(RuntimeException::class);
52+
$this->expectExceptionMessage('invalid json string');
53+
54+
$mockHandler = $this->createMock(CurlMultiHandler::class);
55+
$mockHandler->method('contentIterator')
56+
->willReturn(new ArrayIterator(['{invalid json']));
57+
58+
$stream = new Stream($mockHandler);
59+
$stream->getJsonData();
60+
}
61+
62+
public function testThrowsExceptionIfJsonIsNotArray(): void
63+
{
64+
$this->expectException(RuntimeException::class);
65+
$this->expectExceptionMessage("json data is from type 'boolean', expected an array");
66+
67+
$mockHandler = $this->createMock(CurlMultiHandler::class);
68+
$mockHandler->method('contentIterator')
69+
->willReturn(new ArrayIterator(['true']));
70+
71+
$stream = new Stream($mockHandler);
72+
$stream->getJsonData();
73+
}
74+
75+
public function testReturnsEmptyArrayOnEmptyContents(): void
76+
{
77+
$mockHandler = $this->createMock(CurlMultiHandler::class);
78+
$mockHandler->method('contentIterator')
79+
->willReturn(new ArrayIterator([]));
80+
81+
$stream = new Stream($mockHandler);
82+
83+
$this->assertSame([], $stream->getJsonData());
84+
}
85+
86+
public function testReturnsDecodedArrayIfValidJsonArray(): void
87+
{
88+
$mockHandler = $this->createMock(CurlMultiHandler::class);
89+
$mockHandler->method('contentIterator')
90+
->willReturn(new ArrayIterator(['{"foo":"bar"}']));
91+
92+
$stream = new Stream($mockHandler);
93+
94+
$this->assertSame([
95+
'foo' => 'bar',
96+
], $stream->getJsonData());
97+
}
4798
}

tests/WriteEventsTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,14 @@ public function testSupportsTheIsSubjectPristinePrecondition(): void
9494

9595
$this->expectExceptionMessage("Failed to write events, got HTTP status code '409', expected '200'");
9696

97-
iterator_to_array($this->client->writeEvents(
97+
$this->client->writeEvents(
9898
[
9999
$secondEvent,
100100
],
101101
[
102102
new IsSubjectPristine('/test'),
103103
],
104-
));
104+
);
105105
}
106106

107107
public function testSupportsTheIsSubjectOnEventIdPrecondition(): void
@@ -129,14 +129,14 @@ public function testSupportsTheIsSubjectOnEventIdPrecondition(): void
129129
);
130130

131131
$this->expectExceptionMessage("Failed to write events, got HTTP status code '409', expected '200'");
132-
iterator_to_array($this->client->writeEvents(
132+
$this->client->writeEvents(
133133
[
134134
$secondEvent,
135135
],
136136
[
137137
new IsSubjectOnEventId('/test', '1'),
138138
],
139-
));
139+
);
140140
}
141141

142142
public function testSupportsTheIsEventQlTruePrecondition(): void
@@ -164,13 +164,13 @@ public function testSupportsTheIsEventQlTruePrecondition(): void
164164
);
165165

166166
$this->expectExceptionMessage("Failed to write events, got HTTP status code '409', expected '200'");
167-
iterator_to_array($this->client->writeEvents(
167+
$this->client->writeEvents(
168168
[
169169
$secondEvent,
170170
],
171171
[
172172
new IsEventQlTrue('FROM e IN events PROJECT INTO COUNT() == 0'),
173173
],
174-
));
174+
);
175175
}
176176
}

0 commit comments

Comments
 (0)