Skip to content

Commit 6a035cd

Browse files
authored
feat: Add isSubjectPopulated precondition (#156)
1 parent 039a945 commit 6a035cd

File tree

5 files changed

+101
-3
lines changed

5 files changed

+101
-3
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,23 @@ written_events = await client.write_events(
8686
)
8787
```
8888

89+
#### Using the `isSubjectPopulated` precondition
90+
91+
If you only want to write events in case a subject (such as `/books/42`) already has at least one event, import the `IsSubjectPopulated` class and pass it as the second argument as a list of preconditions:
92+
93+
```python
94+
from eventsourcingdb import IsSubjectPopulated
95+
96+
written_events = await client.write_events(
97+
events = [
98+
# events
99+
],
100+
preconditions = [
101+
IsSubjectPopulated('/books/42')
102+
],
103+
)
104+
```
105+
89106
#### Using the `isSubjectOnEventId` precondition
90107

91108
If you only want to write events in case the last event of a subject (such as `/books/42`) has a specific ID (e.g., `0`), import the `IsSubjectOnEventId` class and pass it as a list of preconditions in the second argument:

eventsourcingdb/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
1010
)
1111
from .read_event_types import EventType
1212
from .read_events import IfEventIsMissingDuringRead, Order, ReadEventsOptions, ReadFromLatestEvent
13-
from .write_events import IsEventQlQueryTrue, IsSubjectOnEventId, IsSubjectPristine, Precondition
13+
from .write_events import (
14+
IsEventQlQueryTrue,
15+
IsSubjectOnEventId,
16+
IsSubjectPopulated,
17+
IsSubjectPristine,
18+
Precondition,
19+
)
1420

1521
__all__ = [
1622
"Bound",
@@ -27,6 +33,7 @@
2733
"InternalError",
2834
"IsEventQlQueryTrue",
2935
"IsSubjectOnEventId",
36+
"IsSubjectPopulated",
3037
"IsSubjectPristine",
3138
"ObserveEventsOptions",
3239
"ObserveFromLatestEvent",
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
from .preconditions import IsEventQlQueryTrue, IsSubjectOnEventId, IsSubjectPristine, Precondition
1+
from .preconditions import (
2+
IsEventQlQueryTrue,
3+
IsSubjectOnEventId,
4+
IsSubjectPopulated,
5+
IsSubjectPristine,
6+
Precondition,
7+
)
28

39
__all__ = [
410
"IsEventQlQueryTrue",
511
"IsSubjectOnEventId",
12+
"IsSubjectPopulated",
613
"IsSubjectPristine",
714
"Precondition",
815
]

eventsourcingdb/write_events/preconditions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ def to_json(self) -> dict[str, Any]:
1717
return {"type": "isSubjectPristine", "payload": {"subject": self.subject}}
1818

1919

20+
@dataclass
21+
class IsSubjectPopulated(Precondition):
22+
subject: str
23+
24+
def to_json(self) -> dict[str, Any]:
25+
return {"type": "isSubjectPopulated", "payload": {"subject": self.subject}}
26+
27+
2028
@dataclass
2129
class IsSubjectOnEventId(Precondition):
2230
subject: str

tests/test_write_events.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import pytest
22
from aiohttp import ClientConnectorDNSError
33

4-
from eventsourcingdb import EventCandidate, IsEventQlQueryTrue, IsSubjectOnEventId, IsSubjectPristine, ServerError
4+
from eventsourcingdb import (
5+
EventCandidate,
6+
IsEventQlQueryTrue,
7+
IsSubjectOnEventId,
8+
IsSubjectPopulated,
9+
IsSubjectPristine,
10+
ServerError,
11+
)
512

613
from .conftest import TestData
714
from .shared.database import Database
@@ -130,6 +137,58 @@ async def test_is_pristine_precondition_fails_for_existing_subject(
130137
[IsSubjectPristine("/")],
131138
)
132139

140+
@staticmethod
141+
@pytest.mark.asyncio
142+
async def test_is_subject_populated_precondition_fails_for_empty_subject(
143+
database: Database,
144+
test_data: TestData,
145+
) -> None:
146+
client = database.get_client()
147+
148+
with pytest.raises(ServerError):
149+
await client.write_events(
150+
[
151+
EventCandidate(
152+
source=test_data.TEST_SOURCE_STRING,
153+
subject="/",
154+
type="com.foo.bar",
155+
data={},
156+
)
157+
],
158+
[IsSubjectPopulated("/")],
159+
)
160+
161+
@staticmethod
162+
@pytest.mark.asyncio
163+
async def test_is_subject_populated_precondition_works_for_existing_subject(
164+
database: Database,
165+
test_data: TestData,
166+
) -> None:
167+
client = database.get_client()
168+
169+
await client.write_events(
170+
[
171+
EventCandidate(
172+
source=test_data.TEST_SOURCE_STRING,
173+
subject="/",
174+
type="com.foo.bar",
175+
data={"value": 23},
176+
)
177+
]
178+
)
179+
180+
await client.write_events(
181+
[
182+
EventCandidate(
183+
source=test_data.TEST_SOURCE_STRING,
184+
subject="/",
185+
type="com.foo.bar",
186+
data={"value": 42},
187+
)
188+
],
189+
[IsSubjectPopulated("/")],
190+
)
191+
133192
@staticmethod
134193
@pytest.mark.asyncio
135194
async def test_is_on_event_id_precondition_works_for_correct_id(

0 commit comments

Comments
 (0)