Skip to content

Commit c7b714c

Browse files
committed
Extended Emmett appending events and getting state exercises with PostgreSQL and EventStoreDB
1 parent 048245d commit c7b714c

File tree

15 files changed

+3871
-3
lines changed

15 files changed

+3871
-3
lines changed

workshops/introduction_to_event_sourcing/src/04_appending_events_emmett/eventstoredb/appendingEvents.exercise.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { getEventStoreDBTestClient } from '#core/testing/eventStoreDB';
1+
import {
2+
getEventStoreDBTestClient,
3+
releaseEventStoreDBContainer,
4+
} from '#core/testing/eventStoreDB';
25
import { type Event } from '@event-driven-io/emmett';
36
import {
47
type EventStoreDBEventStore,
@@ -81,6 +84,8 @@ describe('Appending events', () => {
8184
eventStore = getEventStoreDBEventStore(client);
8285
});
8386

87+
afterAll(() => releaseEventStoreDBContainer());
88+
8489
it('should append events to EventStoreDB', async () => {
8590
const shoppingCartId = uuid();
8691
const clientId = uuid();

workshops/introduction_to_event_sourcing/src/04_appending_events_emmett/postgresql/appendingEvents.exercise.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type Event } from '@event-driven-io/emmett';
22
import {
33
getPostgreSQLEventStore,
44
type PostgresEventStore,
5-
} from '@event-driven-io/emmett-postgresql/.';
5+
} from '@event-driven-io/emmett-postgresql';
66
import { v4 as uuid } from 'uuid';
77
import {
88
getPostgreSQLConnectionString,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import {
2+
getEventStoreDBTestClient,
3+
releaseEventStoreDBContainer,
4+
} from '#core/testing/eventStoreDB';
5+
import { type Event } from '@event-driven-io/emmett';
6+
import {
7+
type EventStoreDBEventStore,
8+
getEventStoreDBEventStore,
9+
} from '@event-driven-io/emmett-esdb';
10+
import { v4 as uuid } from 'uuid';
11+
12+
export interface ProductItem {
13+
productId: string;
14+
quantity: number;
15+
}
16+
17+
export type PricedProductItem = ProductItem & {
18+
unitPrice: number;
19+
};
20+
21+
export type ShoppingCartOpened = Event<
22+
'ShoppingCartOpened',
23+
{
24+
shoppingCartId: string;
25+
clientId: string;
26+
openedAt: Date;
27+
}
28+
>;
29+
30+
export type ProductItemAddedToShoppingCart = Event<
31+
'ProductItemAddedToShoppingCart',
32+
{
33+
shoppingCartId: string;
34+
productItem: PricedProductItem;
35+
}
36+
>;
37+
38+
export type ProductItemRemovedFromShoppingCart = Event<
39+
'ProductItemRemovedFromShoppingCart',
40+
{
41+
shoppingCartId: string;
42+
productItem: PricedProductItem;
43+
}
44+
>;
45+
46+
export type ShoppingCartConfirmed = Event<
47+
'ShoppingCartConfirmed',
48+
{
49+
shoppingCartId: string;
50+
confirmedAt: Date;
51+
}
52+
>;
53+
54+
export type ShoppingCartCanceled = Event<
55+
'ShoppingCartCanceled',
56+
{
57+
shoppingCartId: string;
58+
canceledAt: Date;
59+
}
60+
>;
61+
62+
export type ShoppingCartEvent =
63+
| ShoppingCartOpened
64+
| ProductItemAddedToShoppingCart
65+
| ProductItemRemovedFromShoppingCart
66+
| ShoppingCartConfirmed
67+
| ShoppingCartCanceled;
68+
69+
enum ShoppingCartStatus {
70+
Pending = 'Pending',
71+
Confirmed = 'Confirmed',
72+
Canceled = 'Canceled',
73+
}
74+
75+
export type ShoppingCart = Readonly<{
76+
id: string;
77+
clientId: string;
78+
status: ShoppingCartStatus;
79+
productItems: PricedProductItem[];
80+
openedAt: Date;
81+
confirmedAt?: Date;
82+
canceledAt?: Date;
83+
}>;
84+
85+
const appendToStream = async (
86+
eventStore: EventStoreDBEventStore,
87+
streamName: string,
88+
events: ShoppingCartEvent[],
89+
): Promise<bigint> => {
90+
const { nextExpectedStreamVersion } = await eventStore.appendToStream(
91+
streamName,
92+
events,
93+
);
94+
95+
return nextExpectedStreamVersion;
96+
};
97+
98+
export const getShoppingCart = (
99+
_eventStore: EventStoreDBEventStore,
100+
_streamName: string,
101+
): Promise<ShoppingCart> => {
102+
// 1. Add logic here
103+
return Promise.reject(new Error('Not implemented!'));
104+
};
105+
106+
describe('Events definition', () => {
107+
let eventStore: EventStoreDBEventStore;
108+
109+
beforeAll(async () => {
110+
const client = await getEventStoreDBTestClient();
111+
112+
eventStore = getEventStoreDBEventStore(client);
113+
});
114+
115+
afterAll(() => releaseEventStoreDBContainer());
116+
117+
it('all event types should be defined', async () => {
118+
const shoppingCartId = uuid();
119+
120+
const clientId = uuid();
121+
const openedAt = new Date();
122+
const confirmedAt = new Date();
123+
const canceledAt = new Date();
124+
125+
const shoesId = uuid();
126+
127+
const twoPairsOfShoes: PricedProductItem = {
128+
productId: shoesId,
129+
quantity: 2,
130+
unitPrice: 100,
131+
};
132+
const pairOfShoes: PricedProductItem = {
133+
productId: shoesId,
134+
quantity: 1,
135+
unitPrice: 100,
136+
};
137+
138+
const tShirtId = uuid();
139+
const tShirt: PricedProductItem = {
140+
productId: tShirtId,
141+
quantity: 1,
142+
unitPrice: 5,
143+
};
144+
145+
const events: ShoppingCartEvent[] = [
146+
// 2. Put your sample events here
147+
{
148+
type: 'ShoppingCartOpened',
149+
data: {
150+
shoppingCartId,
151+
clientId,
152+
openedAt,
153+
},
154+
},
155+
{
156+
type: 'ProductItemAddedToShoppingCart',
157+
data: {
158+
shoppingCartId,
159+
productItem: twoPairsOfShoes,
160+
},
161+
},
162+
{
163+
type: 'ProductItemAddedToShoppingCart',
164+
data: {
165+
shoppingCartId,
166+
productItem: tShirt,
167+
},
168+
},
169+
{
170+
type: 'ProductItemRemovedFromShoppingCart',
171+
data: { shoppingCartId, productItem: pairOfShoes },
172+
},
173+
{
174+
type: 'ShoppingCartConfirmed',
175+
data: {
176+
shoppingCartId,
177+
confirmedAt,
178+
},
179+
},
180+
{
181+
type: 'ShoppingCartCanceled',
182+
data: {
183+
shoppingCartId,
184+
canceledAt,
185+
},
186+
},
187+
];
188+
189+
const streamName = `shopping_cart:${shoppingCartId}`;
190+
191+
await appendToStream(eventStore, streamName, events);
192+
193+
const shoppingCart = await getShoppingCart(eventStore, streamName);
194+
195+
expect(shoppingCart).toBe({
196+
id: shoppingCartId,
197+
clientId,
198+
status: ShoppingCartStatus.Canceled,
199+
productItems: [pairOfShoes, tShirt],
200+
openedAt,
201+
confirmedAt,
202+
canceledAt,
203+
});
204+
});
205+
});

0 commit comments

Comments
 (0)