Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/init-dependencies/event-store/commit-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,15 @@ const constructArgsForNewEventRow = (

const insertEventRow = `
INSERT INTO events
(id, resource_id, resource_type, resource_version, event_type, payload)
SELECT ?, ?, ?, ?, ?, ?
(id, event_index, resource_id, resource_type, resource_version, event_type, payload)
SELECT
?,
COALESCE((SELECT MAX(event_index) + 1 FROM events), 1),
?,
?,
?,
?,
?
WHERE NOT EXISTS (
SELECT * FROM events
WHERE resource_id = ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const ensureEventTableExists = (eventDB: Client) =>
`
CREATE TABLE IF NOT EXISTS events (
id TEXT,
event_index number integer NOT NULL UNIQUE,
resource_version number,
resource_id TEXT,
resource_type TEXT,
Expand Down
1 change: 1 addition & 0 deletions src/init-dependencies/event-store/events-from-rows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const reshapeRowToEvent = (row: EventsTable['rows'][number]) =>
tt.JsonFromString.decode,
E.chain(tt.JsonRecord.decode),
E.map(payload => ({
event_index: row.event_index,
event_id: row.id,
type: row.event_type,
...payload,
Expand Down
1 change: 1 addition & 0 deletions src/init-dependencies/event-store/events-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as t from 'io-ts';
export const EventsTable = t.strict({
rows: t.readonlyArray(
t.strict({
event_index: t.number,
id: t.string,
resource_id: t.string,
resource_type: t.string,
Expand Down
12 changes: 7 additions & 5 deletions src/init-dependencies/event-store/get-all-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const getAllEvents =
() =>
dbExecute(
dbClient,
"SELECT * FROM events WHERE event_type != 'EquipmentTrainingQuizResult'",
"SELECT * FROM events WHERE event_type != 'EquipmentTrainingQuizResult' ORDER BY event_index ASC",
{}
),
failureWithStatus(
Expand All @@ -46,9 +46,11 @@ export const getAllEventsByType =
pipe(
TE.tryCatch(
() =>
dbExecute(dbClient, 'SELECT * FROM events WHERE event_type = ?;', [
eventType,
]),
dbExecute(
dbClient,
'SELECT * FROM events WHERE event_type = ? ORDER BY event_index ASC;',
[eventType]
),
failureWithStatus(
`Failed to query database for events of type '${eventType}'`,
StatusCodes.INTERNAL_SERVER_ERROR
Expand Down Expand Up @@ -85,7 +87,7 @@ export const getAllEventsByTypes =
() =>
dbExecute(
dbClient,
'SELECT * FROM events WHERE event_type = ? OR event_type = ?',
'SELECT * FROM events WHERE event_type = ? OR event_type = ? ORDER BY event_index ASC',
[eventType, eventType2]
),
failureWithStatus(
Expand Down
2 changes: 1 addition & 1 deletion src/init-dependencies/event-store/get-resource-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const getResourceEvents =
() =>
dbExecute(
dbClient,
'SELECT * FROM events WHERE resource_type = ? AND resource_id = ?;',
'SELECT * FROM events WHERE resource_type = ? AND resource_id = ? ORDER BY event_index ASC;',
[resource.type, resource.id]
),
failureWithStatus(
Expand Down
3 changes: 2 additions & 1 deletion src/queries/log/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as qs from 'qs';

const renderPayload = (event: ViewModel['events'][number]) =>
// eslint-disable-next-line unused-imports/no-unused-vars
pipe(event, ({type, actor, recordedAt, event_id, ...payload}) =>
pipe(event, ({type, actor, recordedAt, event_index, event_id, ...payload}) =>
pipe(
payload,
Object.entries,
Expand All @@ -24,6 +24,7 @@ const renderEntry = (event: ViewModel['events'][number]) => html`
<li>
<b>${sanitizeString(event.type)}</b> by ${renderActor(event.actor)} at
${displayDate(DateTime.fromJSDate(event.recordedAt))}<br />
Event Index: ${sanitizeString(String(event.event_index))}<br />
Event ID: ${sanitizeString(event.event_id)}<br />
${renderPayload(event)}
</li>
Expand Down
1 change: 1 addition & 0 deletions src/types/domain-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ export const DomainEvent = t.union([
export const StoredDomainEvent = t.intersection([
DomainEvent,
t.strict({
event_index: t.number,
event_id: tt.UUID,
}),
]);
Expand Down
16 changes: 9 additions & 7 deletions tests/init-dependencies/event-store/end-to-end.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ const dummyRefreshReadModel = () => T.of(undefined);

const expectStoredEvent = (
actualEvent: StoredDomainEvent,
expectedEvent: DomainEvent
expectedEvent: DomainEvent,
expectedIndex: number
) => {
expect(actualEvent).toMatchObject(expectedEvent);
expect(actualEvent.event_index).toStrictEqual(expectedIndex);
expect(actualEvent.event_id).toEqual(expect.any(String));
};

Expand Down Expand Up @@ -97,7 +99,7 @@ describe('event-store end-to-end', () => {
it('persists the event', async () => {
const events = await getTestEvents();
expect(events).toHaveLength(1);
expectStoredEvent(events[0], event);
expectStoredEvent(events[0], event, 1);
});

it('uses the initial version number', () => {
Expand All @@ -121,8 +123,8 @@ describe('event-store end-to-end', () => {
it('persists the event', async () => {
const events = await getTestEvents();
expect(events).toHaveLength(2);
expectStoredEvent(events[0], event);
expectStoredEvent(events[1], event2);
expectStoredEvent(events[0], event, 1);
expectStoredEvent(events[1], event2, 2);
});

it('increments the version', () => {
Expand Down Expand Up @@ -153,8 +155,8 @@ describe('event-store end-to-end', () => {
it('does not persist the event', async () => {
const events = await getTestEvents();
expect(events).toHaveLength(2);
expectStoredEvent(events[0], initialEvent);
expectStoredEvent(events[1], competingEvent);
expectStoredEvent(events[0], initialEvent, 1);
expectStoredEvent(events[1], competingEvent, 2);
});

it.failing('returns on left', () => {
Expand Down Expand Up @@ -193,7 +195,7 @@ describe('event-store end-to-end', () => {
it('has independant events', async () => {
expect(await getTestEvents()).toHaveLength(3);
expect(resourceEvents.events).toHaveLength(1);
expectStoredEvent(resourceEvents.events[0], event);
expectStoredEvent(resourceEvents.events[0], event, 3);
});
});
});
Expand Down
22 changes: 12 additions & 10 deletions tests/init-dependencies/event-store/get-all-events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ describe('get all events', () => {

const expectStoredEvent = (
actualEvent: StoredDomainEvent,
expectedEvent: DomainEvent
expectedEvent: DomainEvent,
expectedIndex: number
) => {
expect(actualEvent).toMatchObject(expectedEvent);
expect(actualEvent.event_index).toStrictEqual(expectedIndex);
expect(actualEvent.event_id).toEqual(expect.any(String));
};

Expand Down Expand Up @@ -103,8 +105,8 @@ describe('get all events', () => {
await persistEvent(equipmentTrainingSheetRegistered);
const events = await initalisedGetAllEvents();
expect(events).toHaveLength(2);
expectStoredEvent(events[0], memberNumberLinkedToEmail);
expectStoredEvent(events[1], equipmentTrainingSheetRegistered);
expectStoredEvent(events[0], memberNumberLinkedToEmail, 1);
expectStoredEvent(events[1], equipmentTrainingSheetRegistered, 3);
});
});

Expand All @@ -120,8 +122,8 @@ describe('get all events', () => {
'MemberNumberLinkedToEmail'
);
expect(events).toHaveLength(2);
expectStoredEvent(events[0], firstMatchingEvent);
expectStoredEvent(events[1], secondMatchingEvent);
expectStoredEvent(events[0], firstMatchingEvent, 1);
expectStoredEvent(events[1], secondMatchingEvent, 3);
});

it('returns EquipmentTrainingQuizResult events when explicitly requested', async () => {
Expand All @@ -134,7 +136,7 @@ describe('get all events', () => {
'EquipmentTrainingQuizResult'
);
expect(events).toHaveLength(1);
expectStoredEvent(events[0], equipmentTrainingQuizResult);
expectStoredEvent(events[0], equipmentTrainingQuizResult, 1);
});
});

Expand All @@ -153,8 +155,8 @@ describe('get all events', () => {
'EquipmentTrainingSheetRegistered'
);
expect(events).toHaveLength(2);
expectStoredEvent(events[0], firstMatchingEvent);
expectStoredEvent(events[1], secondMatchingEvent);
expectStoredEvent(events[0], firstMatchingEvent, 1);
expectStoredEvent(events[1], secondMatchingEvent, 3);
});

it('returns EquipmentTrainingQuizResult events when one of the requested types matches', async () => {
Expand All @@ -172,8 +174,8 @@ describe('get all events', () => {
'EquipmentTrainingSheetRegistered'
);
expect(events).toHaveLength(2);
expectStoredEvent(events[0], equipmentTrainingQuizResult);
expectStoredEvent(events[1], matchingEvent);
expectStoredEvent(events[0], equipmentTrainingQuizResult, 1);
expectStoredEvent(events[1], matchingEvent, 2);
});
});
});
4 changes: 4 additions & 0 deletions tests/queries/log/render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('/event-log render', () => {
},
events: [
{
event_index: 42,
event_id: 'cb5bdc6d-f734-43e2-a025-b5d89a5ba3fc' as UUID,
type: 'AreaCreated',
actor: {tag: 'system'},
Expand All @@ -37,7 +38,10 @@ describe('/event-log render', () => {
const page = renderPage(viewModel);

expect(page.textContent).toContain('Event ID:');
expect(page.textContent).toContain('Event Index:');
expect(page.textContent).toContain('42');
expect(page.textContent).toContain('cb5bdc6d-f734-43e2-a025-b5d89a5ba3fc');
expect(page.textContent).not.toContain('event_id:');
expect(page.textContent).not.toContain('event_index:');
});
});
2 changes: 2 additions & 0 deletions tests/types/domain-event.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('DomainEvent', () => {

it('validates stored events with event ids', () => {
const event: unknown = {
event_index: 1,
event_id: 'cb5bdc6d-f734-43e2-a025-b5d89a5ba3fc',
type: 'AreaCreated',
actor: {tag: 'system'},
Expand All @@ -61,6 +62,7 @@ describe('DomainEvent', () => {

const decoded = unwrap(StoredDomainEvent.decode(event));
expect(decoded).toEqual({
event_index: 1,
event_id: 'cb5bdc6d-f734-43e2-a025-b5d89a5ba3fc',
actor: {tag: 'system'},
id: 'd1428735-0482-49c4-b16b-82503ccea74b',
Expand Down
Loading