Skip to content

Commit 82cd960

Browse files
authored
Add a global event id (#187)
* Add a global event id * NOT NULL for event_index
1 parent a683737 commit 82cd960

File tree

12 files changed

+50
-26
lines changed

12 files changed

+50
-26
lines changed

src/init-dependencies/event-store/commit-event.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,15 @@ const constructArgsForNewEventRow = (
3535

3636
const insertEventRow = `
3737
INSERT INTO events
38-
(id, resource_id, resource_type, resource_version, event_type, payload)
39-
SELECT ?, ?, ?, ?, ?, ?
38+
(id, event_index, resource_id, resource_type, resource_version, event_type, payload)
39+
SELECT
40+
?,
41+
COALESCE((SELECT MAX(event_index) + 1 FROM events), 1),
42+
?,
43+
?,
44+
?,
45+
?,
46+
?
4047
WHERE NOT EXISTS (
4148
SELECT * FROM events
4249
WHERE resource_id = ?

src/init-dependencies/event-store/ensure-events-table-exists.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const ensureEventTableExists = (eventDB: Client) =>
1111
`
1212
CREATE TABLE IF NOT EXISTS events (
1313
id TEXT,
14+
event_index number integer NOT NULL UNIQUE,
1415
resource_version number,
1516
resource_id TEXT,
1617
resource_type TEXT,

src/init-dependencies/event-store/events-from-rows.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const reshapeRowToEvent = (row: EventsTable['rows'][number]) =>
1212
tt.JsonFromString.decode,
1313
E.chain(tt.JsonRecord.decode),
1414
E.map(payload => ({
15+
event_index: row.event_index,
1516
event_id: row.id,
1617
type: row.event_type,
1718
...payload,

src/init-dependencies/event-store/events-table.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as t from 'io-ts';
33
export const EventsTable = t.strict({
44
rows: t.readonlyArray(
55
t.strict({
6+
event_index: t.number,
67
id: t.string,
78
resource_id: t.string,
89
resource_type: t.string,

src/init-dependencies/event-store/get-all-events.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const getAllEvents =
2222
() =>
2323
dbExecute(
2424
dbClient,
25-
"SELECT * FROM events WHERE event_type != 'EquipmentTrainingQuizResult'",
25+
"SELECT * FROM events WHERE event_type != 'EquipmentTrainingQuizResult' ORDER BY event_index ASC",
2626
{}
2727
),
2828
failureWithStatus(
@@ -46,9 +46,11 @@ export const getAllEventsByType =
4646
pipe(
4747
TE.tryCatch(
4848
() =>
49-
dbExecute(dbClient, 'SELECT * FROM events WHERE event_type = ?;', [
50-
eventType,
51-
]),
49+
dbExecute(
50+
dbClient,
51+
'SELECT * FROM events WHERE event_type = ? ORDER BY event_index ASC;',
52+
[eventType]
53+
),
5254
failureWithStatus(
5355
`Failed to query database for events of type '${eventType}'`,
5456
StatusCodes.INTERNAL_SERVER_ERROR
@@ -85,7 +87,7 @@ export const getAllEventsByTypes =
8587
() =>
8688
dbExecute(
8789
dbClient,
88-
'SELECT * FROM events WHERE event_type = ? OR event_type = ?',
90+
'SELECT * FROM events WHERE event_type = ? OR event_type = ? ORDER BY event_index ASC',
8991
[eventType, eventType2]
9092
),
9193
failureWithStatus(

src/init-dependencies/event-store/get-resource-events.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const getResourceEvents =
2929
() =>
3030
dbExecute(
3131
dbClient,
32-
'SELECT * FROM events WHERE resource_type = ? AND resource_id = ?;',
32+
'SELECT * FROM events WHERE resource_type = ? AND resource_id = ? ORDER BY event_index ASC;',
3333
[resource.type, resource.id]
3434
),
3535
failureWithStatus(

src/queries/log/render.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as qs from 'qs';
1010

1111
const renderPayload = (event: ViewModel['events'][number]) =>
1212
// eslint-disable-next-line unused-imports/no-unused-vars
13-
pipe(event, ({type, actor, recordedAt, event_id, ...payload}) =>
13+
pipe(event, ({type, actor, recordedAt, event_index, event_id, ...payload}) =>
1414
pipe(
1515
payload,
1616
Object.entries,
@@ -24,6 +24,7 @@ const renderEntry = (event: ViewModel['events'][number]) => html`
2424
<li>
2525
<b>${sanitizeString(event.type)}</b> by ${renderActor(event.actor)} at
2626
${displayDate(DateTime.fromJSDate(event.recordedAt))}<br />
27+
Event Index: ${sanitizeString(String(event.event_index))}<br />
2728
Event ID: ${sanitizeString(event.event_id)}<br />
2829
${renderPayload(event)}
2930
</li>

src/types/domain-event.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ export const DomainEvent = t.union([
286286
export const StoredDomainEvent = t.intersection([
287287
DomainEvent,
288288
t.strict({
289+
event_index: t.number,
289290
event_id: tt.UUID,
290291
}),
291292
]);

tests/init-dependencies/event-store/end-to-end.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ const dummyRefreshReadModel = () => T.of(undefined);
3636

3737
const expectStoredEvent = (
3838
actualEvent: StoredDomainEvent,
39-
expectedEvent: DomainEvent
39+
expectedEvent: DomainEvent,
40+
expectedIndex: number
4041
) => {
4142
expect(actualEvent).toMatchObject(expectedEvent);
43+
expect(actualEvent.event_index).toStrictEqual(expectedIndex);
4244
expect(actualEvent.event_id).toEqual(expect.any(String));
4345
};
4446

@@ -97,7 +99,7 @@ describe('event-store end-to-end', () => {
9799
it('persists the event', async () => {
98100
const events = await getTestEvents();
99101
expect(events).toHaveLength(1);
100-
expectStoredEvent(events[0], event);
102+
expectStoredEvent(events[0], event, 1);
101103
});
102104

103105
it('uses the initial version number', () => {
@@ -121,8 +123,8 @@ describe('event-store end-to-end', () => {
121123
it('persists the event', async () => {
122124
const events = await getTestEvents();
123125
expect(events).toHaveLength(2);
124-
expectStoredEvent(events[0], event);
125-
expectStoredEvent(events[1], event2);
126+
expectStoredEvent(events[0], event, 1);
127+
expectStoredEvent(events[1], event2, 2);
126128
});
127129

128130
it('increments the version', () => {
@@ -153,8 +155,8 @@ describe('event-store end-to-end', () => {
153155
it('does not persist the event', async () => {
154156
const events = await getTestEvents();
155157
expect(events).toHaveLength(2);
156-
expectStoredEvent(events[0], initialEvent);
157-
expectStoredEvent(events[1], competingEvent);
158+
expectStoredEvent(events[0], initialEvent, 1);
159+
expectStoredEvent(events[1], competingEvent, 2);
158160
});
159161

160162
it.failing('returns on left', () => {
@@ -193,7 +195,7 @@ describe('event-store end-to-end', () => {
193195
it('has independant events', async () => {
194196
expect(await getTestEvents()).toHaveLength(3);
195197
expect(resourceEvents.events).toHaveLength(1);
196-
expectStoredEvent(resourceEvents.events[0], event);
198+
expectStoredEvent(resourceEvents.events[0], event, 3);
197199
});
198200
});
199201
});

tests/init-dependencies/event-store/get-all-events.test.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ describe('get all events', () => {
5252

5353
const expectStoredEvent = (
5454
actualEvent: StoredDomainEvent,
55-
expectedEvent: DomainEvent
55+
expectedEvent: DomainEvent,
56+
expectedIndex: number
5657
) => {
5758
expect(actualEvent).toMatchObject(expectedEvent);
59+
expect(actualEvent.event_index).toStrictEqual(expectedIndex);
5860
expect(actualEvent.event_id).toEqual(expect.any(String));
5961
};
6062

@@ -103,8 +105,8 @@ describe('get all events', () => {
103105
await persistEvent(equipmentTrainingSheetRegistered);
104106
const events = await initalisedGetAllEvents();
105107
expect(events).toHaveLength(2);
106-
expectStoredEvent(events[0], memberNumberLinkedToEmail);
107-
expectStoredEvent(events[1], equipmentTrainingSheetRegistered);
108+
expectStoredEvent(events[0], memberNumberLinkedToEmail, 1);
109+
expectStoredEvent(events[1], equipmentTrainingSheetRegistered, 3);
108110
});
109111
});
110112

@@ -120,8 +122,8 @@ describe('get all events', () => {
120122
'MemberNumberLinkedToEmail'
121123
);
122124
expect(events).toHaveLength(2);
123-
expectStoredEvent(events[0], firstMatchingEvent);
124-
expectStoredEvent(events[1], secondMatchingEvent);
125+
expectStoredEvent(events[0], firstMatchingEvent, 1);
126+
expectStoredEvent(events[1], secondMatchingEvent, 3);
125127
});
126128

127129
it('returns EquipmentTrainingQuizResult events when explicitly requested', async () => {
@@ -134,7 +136,7 @@ describe('get all events', () => {
134136
'EquipmentTrainingQuizResult'
135137
);
136138
expect(events).toHaveLength(1);
137-
expectStoredEvent(events[0], equipmentTrainingQuizResult);
139+
expectStoredEvent(events[0], equipmentTrainingQuizResult, 1);
138140
});
139141
});
140142

@@ -153,8 +155,8 @@ describe('get all events', () => {
153155
'EquipmentTrainingSheetRegistered'
154156
);
155157
expect(events).toHaveLength(2);
156-
expectStoredEvent(events[0], firstMatchingEvent);
157-
expectStoredEvent(events[1], secondMatchingEvent);
158+
expectStoredEvent(events[0], firstMatchingEvent, 1);
159+
expectStoredEvent(events[1], secondMatchingEvent, 3);
158160
});
159161

160162
it('returns EquipmentTrainingQuizResult events when one of the requested types matches', async () => {
@@ -172,8 +174,8 @@ describe('get all events', () => {
172174
'EquipmentTrainingSheetRegistered'
173175
);
174176
expect(events).toHaveLength(2);
175-
expectStoredEvent(events[0], equipmentTrainingQuizResult);
176-
expectStoredEvent(events[1], matchingEvent);
177+
expectStoredEvent(events[0], equipmentTrainingQuizResult, 1);
178+
expectStoredEvent(events[1], matchingEvent, 2);
177179
});
178180
});
179181
});

0 commit comments

Comments
 (0)