Skip to content

Commit 9264514

Browse files
authored
feat: Add isSubjectPopulated precondition (#285)
1 parent 5a4d282 commit 9264514

File tree

5 files changed

+86
-1
lines changed

5 files changed

+86
-1
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ const writtenEvents = await client.writeEvents([
7878
]);
7979
```
8080

81+
#### Using the `isSubjectPopulated` precondition
82+
83+
If you only want to write events in case a subject (such as `/books/42`) already has at least one event, import the `isSubjectPopulated` function and pass it as an array of preconditions in the second argument:
84+
85+
```typescript
86+
import { isSubjectPopulated } from 'eventsourcingdb';
87+
88+
const writtenEvents = await client.writeEvents([
89+
// events
90+
], [
91+
isSubjectPopulated('/books/42')
92+
]);
93+
```
94+
8195
#### Using the `isSubjectOnEventId` precondition
8296

8397
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` function and pass it as an array of preconditions in the second argument:

src/Precondition.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ interface IsSubjectPristinePrecondition {
22
subject: string;
33
}
44

5+
interface IsSubjectPopulatedPrecondition {
6+
subject: string;
7+
}
8+
59
interface IsSubjectOnEventIdPrecondition {
610
subject: string;
711
eventId: string;
@@ -20,6 +24,10 @@ type Precondition =
2024
type: 'isSubjectOnEventId';
2125
payload: IsSubjectOnEventIdPrecondition;
2226
}
27+
| {
28+
type: 'isSubjectPopulated';
29+
payload: IsSubjectPopulatedPrecondition;
30+
}
2331
| {
2432
type: 'isEventQlQueryTrue';
2533
payload: IsEventQlQueryTruePrecondition;

src/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@ import type { EventCandidate } from './EventCandidate.js';
55
import type { EventType } from './EventType.js';
66
import { isEventQlQueryTrue } from './isEventQlQueryTrue.js';
77
import { isSubjectOnEventId } from './isSubjectOnEventId.js';
8+
import { isSubjectPopulated } from './isSubjectPopulated.js';
89
import { isSubjectPristine } from './isSubjectPristine.js';
910
import type { ObserveEventsOptions } from './ObserveEventsOptions.js';
1011
import type { Precondition } from './Precondition.js';
1112
import type { ReadEventsOptions } from './ReadEventsOptions.js';
1213

13-
export { Client, Container, isEventQlQueryTrue, isSubjectOnEventId, isSubjectPristine };
14+
export {
15+
Client,
16+
Container,
17+
isEventQlQueryTrue,
18+
isSubjectOnEventId,
19+
isSubjectPopulated,
20+
isSubjectPristine,
21+
};
1422
export type {
1523
Event,
1624
EventCandidate,

src/isSubjectPopulated.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { Precondition } from './Precondition.js';
2+
3+
const isSubjectPopulated = (subject: string): Precondition => {
4+
return {
5+
type: 'isSubjectPopulated',
6+
payload: { subject },
7+
};
8+
};
9+
10+
export { isSubjectPopulated };

src/writeEvents.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { EventCandidate } from './EventCandidate.js';
55
import { getImageVersionFromDockerfile } from './getImageVersionFromDockerfile.js';
66
import { isEventQlQueryTrue } from './isEventQlQueryTrue.js';
77
import { isSubjectOnEventId } from './isSubjectOnEventId.js';
8+
import { isSubjectPopulated } from './isSubjectPopulated.js';
89
import { isSubjectPristine } from './isSubjectPristine.js';
910

1011
suite('writeEvents', { timeout: 30_000 }, () => {
@@ -107,6 +108,50 @@ suite('writeEvents', { timeout: 30_000 }, () => {
107108
);
108109
});
109110

111+
test('supports the isSubjectPopulated precondition.', async (): Promise<void> => {
112+
const client = container.getClient();
113+
114+
const firstEvent: EventCandidate = {
115+
source: 'https://www.eventsourcingdb.io',
116+
subject: '/test',
117+
type: 'io.eventsourcingdb.test',
118+
data: {
119+
value: 23,
120+
},
121+
};
122+
123+
const secondEvent: EventCandidate = {
124+
source: 'https://www.eventsourcingdb.io',
125+
subject: '/test',
126+
type: 'io.eventsourcingdb.test',
127+
data: {
128+
value: 42,
129+
},
130+
};
131+
132+
await assert.rejects(
133+
async () => {
134+
await client.writeEvents([secondEvent], [isSubjectPopulated('/test')]);
135+
},
136+
error => {
137+
assert.ok(error instanceof Error);
138+
assert.equal(
139+
error.message,
140+
"Failed to write events, got HTTP status code '409', expected '200'.",
141+
);
142+
return true;
143+
},
144+
);
145+
146+
await client.writeEvents([firstEvent]);
147+
148+
const writtenEvents = await client.writeEvents([secondEvent], [isSubjectPopulated('/test')]);
149+
150+
assert.equal(writtenEvents.length, 1);
151+
assert.equal(writtenEvents[0].id, '1');
152+
assert.equal(writtenEvents[0].data.value, 42);
153+
});
154+
110155
test('supports the isSubjectOnEventId precondition.', async (): Promise<void> => {
111156
const client = container.getClient();
112157

0 commit comments

Comments
 (0)