Skip to content

Commit 5ccba6c

Browse files
committed
Adjusted sample to showcase idempotence handling
It shows idempotence both upon the new stream creation (check in) and existing stream (check out).
1 parent 2bf7f96 commit 5ccba6c

File tree

5 files changed

+28
-59
lines changed

5 files changed

+28
-59
lines changed

src/guestStayAccounts/api/api.e2e.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,10 @@ void describe('guestStayAccount E2E', () => {
147147
void describe('When checked in', () => {
148148
const checkedInAccount: TestRequest = checkIn;
149149

150-
void it(`doesn't check in`, () =>
150+
void it(`ignores check in`, () =>
151151
given(checkedInAccount)
152152
.when(checkIn)
153-
.then([expectError(403, { detail: `Guest is already checked-in!` })]));
153+
.then([expectResponse(201)]));
154154

155155
void it('records charge', () =>
156156
given(checkedInAccount)
@@ -299,10 +299,10 @@ void describe('guestStayAccount E2E', () => {
299299
expectError(403, { detail: `Guest account is already checked out` }),
300300
]));
301301

302-
void it(`doesn't checkout`, () =>
302+
void it(`ignores checkout`, () =>
303303
given(...checkedOutAccount)
304304
.when(checkOut)
305-
.then([expectError(403, { detail: `NotCheckedIn` })]));
305+
.then([expectResponse(204)]));
306306

307307
void it(`details return 404`, () =>
308308
given(...checkedOutAccount)

src/guestStayAccounts/api/api.int.spec.ts

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,8 @@ void describe('Guest stay account', () => {
126126
])),
127127
);
128128

129-
void it(`doesn't check in`, () =>
130-
given(checkedInAccount)
131-
.when(checkIn)
132-
.then(expectError(403, { detail: `Guest is already checked-in!` })));
129+
void it(`ingores check in`, () =>
130+
given(checkedInAccount).when(checkIn).then(expectResponse(201)));
133131

134132
void it('records charge', () =>
135133
given(checkedInAccount)
@@ -422,23 +420,10 @@ void describe('Guest stay account', () => {
422420
expectError(403, { detail: `Guest account is already checked out` }),
423421
));
424422

425-
void it(`doesn't checkout`, () =>
423+
void it(`ignores check out`, () =>
426424
given(checkedOutAccount)
427425
.when(checkOut)
428-
.then([
429-
expectError(403, { detail: `NotCheckedIn` }),
430-
expectNewEvents(guestStayAccountId, [
431-
{
432-
type: 'GuestCheckoutFailed',
433-
data: {
434-
guestStayAccountId,
435-
groupCheckoutId: undefined,
436-
reason: 'NotCheckedIn',
437-
failedAt: now,
438-
},
439-
},
440-
]),
441-
]));
426+
.then([expectResponse(204)]));
442427
});
443428

444429
const given = ApiSpecification.for<GuestStayAccountEvent>(

src/guestStayAccounts/api/api.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,17 @@ export const guestStayAccountsApi =
168168
metadata: { now: getCurrentTime() },
169169
};
170170

171-
const {
172-
newEvents: [event],
173-
} = await handle(eventStore, guestStayAccountId, (state) =>
174-
checkOut(command, state),
171+
const { newEvents } = await handle(
172+
eventStore,
173+
guestStayAccountId,
174+
(state) => checkOut(command, state),
175175
);
176176

177-
return event.type !== 'GuestCheckoutFailed'
177+
return newEvents.length === 0 ||
178+
newEvents[0].type !== 'GuestCheckoutFailed'
178179
? NoContent()
179180
: Forbidden({
180-
problemDetails: event.data.reason,
181+
problemDetails: newEvents[0].data.reason,
181182
});
182183
}),
183184
);

src/guestStayAccounts/businessLogic.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@ export type GuestStayCommand =
5454
export const checkIn = (
5555
{ data: { guestId, roomId }, metadata }: CheckIn,
5656
state: GuestStayAccount,
57-
): GuestCheckedIn => {
58-
assertDoesNotExist(state);
57+
): GuestCheckedIn | [] => {
58+
if (state.status === 'CheckedIn') [];
59+
60+
if (state.status === 'CheckedOut')
61+
throw new IllegalStateError(`Guest account is already checked out`);
5962

6063
const now = metadata?.now ?? new Date();
6164

@@ -107,7 +110,9 @@ export const recordPayment = (
107110
export const checkOut = (
108111
{ data: { guestStayAccountId, groupCheckoutId }, metadata }: CheckOut,
109112
state: GuestStayAccount,
110-
): GuestCheckedOut | GuestCheckoutFailed => {
113+
): GuestCheckedOut | GuestCheckoutFailed | [] => {
114+
if (state.status === 'CheckedOut') return [];
115+
111116
const now = metadata?.now ?? new Date();
112117

113118
if (state.status !== 'CheckedIn')
@@ -146,7 +151,7 @@ export const checkOut = (
146151
export const decide = (
147152
command: GuestStayCommand,
148153
state: GuestStayAccount,
149-
): GuestStayAccountEvent => {
154+
): GuestStayAccountEvent | GuestStayAccountEvent[] => {
150155
const { type } = command;
151156

152157
switch (type) {
@@ -165,16 +170,6 @@ export const decide = (
165170
}
166171
};
167172

168-
const assertDoesNotExist = (state: GuestStayAccount): state is CheckedIn => {
169-
if (state.status === 'CheckedIn')
170-
throw new IllegalStateError(`Guest is already checked-in!`);
171-
172-
if (state.status === 'CheckedOut')
173-
throw new IllegalStateError(`Guest account is already checked out`);
174-
175-
return true;
176-
};
177-
178173
const assertIsCheckedIn = (state: GuestStayAccount): state is CheckedIn => {
179174
if (state.status === 'NotExisting')
180175
throw new IllegalStateError(`Guest account doesn't exist!`);

src/guestStayAccounts/businessLogic.unit.spec.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ void describe('Guest Stay Account', () => {
121121
},
122122
];
123123

124-
void it(`doesn't check in`, () =>
124+
void it(`ignores check in`, () =>
125125
given(checkedInAccount)
126126
.when({
127127
type: 'CheckIn',
@@ -131,9 +131,7 @@ void describe('Guest Stay Account', () => {
131131
},
132132
metadata: { now },
133133
})
134-
.thenThrows<IllegalStateError>(
135-
(error) => error.message === `Guest is already checked-in!`,
136-
));
134+
.then([]));
137135

138136
void it('records charge', () => {
139137
given(checkedInAccount)
@@ -476,7 +474,7 @@ void describe('Guest Stay Account', () => {
476474
(error) => error.message === `Guest account is already checked out`,
477475
));
478476

479-
void it(`doesn't checkout`, () =>
477+
void it(`ignores check out`, () =>
480478
given(checkedOutAccount)
481479
.when({
482480
type: 'CheckOut',
@@ -485,16 +483,6 @@ void describe('Guest Stay Account', () => {
485483
},
486484
metadata: { now },
487485
})
488-
.then([
489-
{
490-
type: 'GuestCheckoutFailed',
491-
data: {
492-
guestStayAccountId,
493-
groupCheckoutId: undefined,
494-
reason: 'NotCheckedIn',
495-
failedAt: now,
496-
},
497-
},
498-
]));
486+
.then([]));
499487
});
500488
});

0 commit comments

Comments
 (0)