Skip to content

Commit e937756

Browse files
wundiiawugoloroden
authored
feat: implement HttpClient and observerEvent (#19)
Co-authored-by: awu <andreas.wunderwald@westpress.de> Co-authored-by: Golo Roden <golo.roden@thenativeweb.io>
1 parent 7de1a8e commit e937756

32 files changed

Lines changed: 3682 additions & 1670 deletions

README.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ To read all events of a subject, call the `readEvents` function with the subject
102102
The function returns an iterator, which you can use in a `foreach` loop:
103103

104104
```php
105+
use Thenativeweb\Eventsourcingdb\ReadEventsOptions;
106+
105107
$events = $client->readEvents(
106108
'/books/42',
107109
new ReadEventsOptions(
@@ -119,6 +121,8 @@ foreach ($events as $event) {
119121
If you want to read not only all the events of a subject, but also the events of all nested subjects, set the `recursive` option to `true`:
120122

121123
```php
124+
use Thenativeweb\Eventsourcingdb\ReadEventsOptions;
125+
122126
$events = $client->readEvents(
123127
'/books/42',
124128
new ReadEventsOptions(
@@ -138,6 +142,9 @@ This also allows you to read *all* events ever written. To do so, provide `/` as
138142
By default, events are read in chronological order. To read in anti-chronological order, provide the `order` option and set it to `Order::ANTICHRONOLOGICAL`:
139143

140144
```php
145+
use Thenativeweb\Eventsourcingdb\Order;
146+
use Thenativeweb\Eventsourcingdb\ReadEventsOptions;
147+
141148
$events = $client->readEvents(
142149
'/books/42',
143150
new ReadEventsOptions(
@@ -160,6 +167,10 @@ Sometimes you do not want to read all events, but only a range of events. For th
160167
Specify the ID and whether to include or exclude it, for both the lower and upper bound:
161168

162169
```php
170+
use Thenativeweb\Eventsourcingdb\Bound;
171+
use Thenativeweb\Eventsourcingdb\BoundType;
172+
use Thenativeweb\Eventsourcingdb\ReadEventsOptions;
173+
163174
$events = $client->readEvents(
164175
'/books/42',
165176
new ReadEventsOptions(
@@ -181,6 +192,10 @@ To read starting from the latest event of a given type, provide the `fromLatestE
181192
Possible options are `ReadIfEventIsMissing::READ_NOTHING`, which skips reading entirely, or `ReadIfEventIsMissing::READ_EVERYTHING`, which effectively behaves as if `fromLatestEvent` was not specified:
182193

183194
```php
195+
use Thenativeweb\Eventsourcingdb\ReadEventsOptions;
196+
use Thenativeweb\Eventsourcingdb\ReadFromLatestEvent;
197+
use Thenativeweb\Eventsourcingdb\ReadIfEventIsMissing;
198+
184199
$events = $client->readEvents(
185200
'/books/42',
186201
new ReadEventsOptions(
@@ -216,6 +231,123 @@ foreach ($rows as $row) {
216231

217232
*Note that each row returned by the iterator is an associative array and matches the projection specified in your query.*
218233

234+
### Observing Events
235+
236+
To observe all events of a subject, call the `observeEvents` function with the subject as the first argument and an options object as the second argument. Set the `recursive` option to `false`. This ensures that only events of the given subject are returned, not events of nested subjects.
237+
238+
The function returns an asynchronous iterator, which you can use e.g. inside a `foreach` loop:
239+
240+
```php
241+
use Thenativeweb\Eventsourcingdb\ObserveEventsOptions;
242+
243+
$events = $client->observeEvents(
244+
'/books/42',
245+
new ObserveEventsOptions(
246+
recursive: false,
247+
),
248+
);
249+
250+
foreach ($events as $event) {
251+
// ...
252+
}
253+
```
254+
255+
#### Observing From Subjects Recursively
256+
257+
If you want to observe not only all the events of a subject, but also the events of all nested subjects, set the `recursive` option to `true`:
258+
259+
```php
260+
use Thenativeweb\Eventsourcingdb\ObserveEventsOptions;
261+
262+
$events = $client->observeEvents(
263+
'/books/42',
264+
new ObserveEventsOptions(
265+
recursive: true,
266+
),
267+
);
268+
269+
foreach ($events as $event) {
270+
// ...
271+
}
272+
```
273+
274+
This also allows you to observe *all* events ever written. To do so, provide `/` as the subject and set `recursive` to `true`, since all subjects are nested under the root subject.
275+
276+
#### Specifying Bounds
277+
278+
Sometimes you do not want to observe all events, but only a range of events. For that, you can specify the `lowerBound` option.
279+
280+
Specify the ID and whether to include or exclude it:
281+
282+
```php
283+
use Thenativeweb\Eventsourcingdb\Bound;
284+
use Thenativeweb\Eventsourcingdb\BoundType;
285+
use Thenativeweb\Eventsourcingdb\ObserveEventsOptions;
286+
287+
$events = $client->observeEvents(
288+
'/books/42',
289+
new ObserveEventsOptions(
290+
recursive: false,
291+
lowerBound: new Bound('100', BoundType::INCLUSIVE),
292+
),
293+
);
294+
295+
foreach ($events as $event) {
296+
// ...
297+
}
298+
```
299+
300+
#### Starting From the Latest Event of a Given Type
301+
302+
To observe starting from the latest event of a given type, provide the `fromLatestEvent` option and specify the subject, the type, and how to proceed if no such event exists.
303+
304+
Possible options are `ObserveIfEventIsMissing::WAIT_FOR_EVENT`, which waits for an event of the given type to happen, or `ObserveIfEventIsMissing::READ_EVERYTHING`, which effectively behaves as if `fromLatestEvent` was not specified:
305+
306+
```php
307+
use Thenativeweb\Eventsourcingdb\ObserveEventsOptions;
308+
use Thenativeweb\Eventsourcingdb\ObserveFromLatestEvent;
309+
use Thenativeweb\Eventsourcingdb\ObserveIfEventIsMissing;
310+
311+
$events = $client->observeEvents(
312+
'/books/42',
313+
new ObserveEventsOptions(
314+
recursive: false,
315+
fromLatestEvent: new ObserveFromLatestEvent(
316+
subject: '/books/42',
317+
type: 'io.eventsourcingdb.library.book-borrowed',
318+
ifEventIsMissing: ObserveIfEventIsMissing::READ_EVERYTHING,
319+
),
320+
),
321+
);
322+
323+
foreach ($events as $event) {
324+
// ...
325+
}
326+
```
327+
328+
*Note that `fromLatestEvent` and `lowerBound` can not be provided at the same time.*
329+
330+
#### Aborting Observing
331+
332+
If you need to abort observing use `abortIn` before or within the `foreach` loop. The `abortIn` method expects the abort time in seconds. However, this only works if there is currently an iteration going on:
333+
334+
```php
335+
use Thenativeweb\Eventsourcingdb\ObserveEventsOptions;
336+
337+
$events = $client->observeEvents(
338+
'/books/42',
339+
new ObserveEventsOptions(
340+
recursive: false,
341+
),
342+
);
343+
344+
$client->abortIn(0.1);
345+
foreach ($events as $event) {
346+
// ...
347+
$client->abortIn(0.1);
348+
}
349+
```
350+
219351
### Using Testcontainers
220352

221353
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:
@@ -246,13 +378,17 @@ $isRunning = $container->isRunning();
246378
By default, `Container` uses the `latest` tag of the official EventSourcingDB Docker image. To change that, call the `withImageTag` function:
247379

248380
```php
381+
use Thenativeweb\Eventsourcingdb\Container;
382+
249383
$container = new Container()
250384
->withImageTag('1.0.0');
251385
```
252386

253387
Similarly, you can configure the port to use and the API token. Call the `withPort` or the `withApiToken` function respectively:
254388

255389
```php
390+
use Thenativeweb\Eventsourcingdb\Container;
391+
256392
$container = new Container()
257393
->withPort(4000)
258394
->withApiToken('secret');
@@ -266,3 +402,4 @@ In case you need to set up the client yourself, use the following functions to g
266402
- `getMappedPort()` returns the port
267403
- `getBaseUrl()` returns the full URL of the container
268404
- `getApiToken()` returns the API token
405+

composer.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525
"minimum-stability": "stable",
2626
"require": {
2727
"php": ">=8.2",
28-
"guzzlehttp/guzzle": "^7.5"
28+
"ext-curl": "*"
2929
},
3030
"require-dev": {
3131
"phpstan/phpstan": "2.1.17",
3232
"phpunit/phpunit": "11.5.21",
3333
"rector/rector": "2.1.1",
34+
"symfony/http-client": "7.3.1",
3435
"symplify/easy-coding-standard": "12.5.20",
3536
"testcontainers/testcontainers": "1.0.3"
3637
},
@@ -49,7 +50,10 @@
4950
"@analyze",
5051
"@test"
5152
],
52-
"test": "vendor/bin/phpunit"
53+
"test": [
54+
"vendor/bin/phpunit --testsuite \"HttpClient\"",
55+
"vendor/bin/phpunit --testsuite \"EsdbClient\""
56+
]
5357
},
5458
"config": {
5559
"allow-plugins": {

0 commit comments

Comments
 (0)