Skip to content

Commit ee6d733

Browse files
authored
feat(command): add StreamNameMapper to ErrorRecorder (#50)
1 parent 9817814 commit ee6d733

2 files changed

Lines changed: 61 additions & 2 deletions

File tree

command/error_recorder.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ type ErrorRecorder struct {
3737
// If unspecified, FailedCommandType will be used by default.
3838
StreamType string
3939

40+
// StreamNameMapper maps to a StreamName value based on the command that failed.
41+
// This is useful in case you want to use the same Aggregate id the command is targeting.
42+
//
43+
// If unspecified, the command name will be used instead.
44+
StreamNameMapper func(cmd eventually.Command) string
45+
4046
// EventMapper should return the Domain Event type you defined for these commands.
4147
//
4248
// NOTE: this is necessary for (de)-serialization purposes while generics are
@@ -53,9 +59,14 @@ func (er ErrorRecorder) streamType() string {
5359
}
5460

5561
func (er ErrorRecorder) buildStreamID(cmd eventually.Command) eventstore.StreamID {
62+
streamName := cmd.Payload.Name()
63+
if er.StreamNameMapper != nil {
64+
streamName = er.StreamNameMapper(cmd)
65+
}
66+
5667
return eventstore.StreamID{
5768
Type: er.streamType(),
58-
Name: cmd.Payload.Name(),
69+
Name: streamName,
5970
}
6071
}
6172

command/error_recorder_test.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ func TestErrorRecorder(t *testing.T) {
145145
eventStore := inmemory.NewEventStore()
146146
trackingEventStore := inmemory.NewTrackingEventStore(eventStore)
147147

148-
expectedStreamType := "mocks-command"
148+
const expectedStreamType = "mocks-command"
149+
149150
expectedErr := errors.New("failed command")
150151
expectedCommand := eventually.Command{
151152
Payload: mockCommand{message: t.Name()},
@@ -184,4 +185,51 @@ func TestErrorRecorder(t *testing.T) {
184185
},
185186
}, trackingEventStore.Recorded())
186187
})
188+
189+
t.Run("when handler fails, record event with custom stream name", func(t *testing.T) {
190+
eventStore := inmemory.NewEventStore()
191+
trackingEventStore := inmemory.NewTrackingEventStore(eventStore)
192+
193+
expectedStreamType := "mocks-command"
194+
expectedErr := errors.New("failed command")
195+
expectedCommand := eventually.Command{
196+
Payload: mockCommand{message: t.Name()},
197+
}
198+
199+
handler := command.ErrorRecorder{
200+
Handler: command.HandlerFunc(func(ctx context.Context, cmd eventually.Command) error {
201+
return expectedErr
202+
}),
203+
Appender: trackingEventStore,
204+
StreamType: expectedStreamType,
205+
StreamNameMapper: func(cmd eventually.Command) string {
206+
return cmd.Payload.(mockCommand).message
207+
},
208+
EventMapper: func(err error, cmd eventually.Command) eventually.Payload {
209+
return mockCommandHasFailed{
210+
err: err,
211+
command: cmd.Payload.(mockCommand),
212+
}
213+
},
214+
}
215+
216+
err := handler.Handle(context.Background(), expectedCommand)
217+
218+
assert.Error(t, err)
219+
assert.Equal(t, []eventstore.Event{
220+
{
221+
Version: 1,
222+
Stream: eventstore.StreamID{
223+
Type: expectedStreamType,
224+
Name: expectedCommand.Payload.(mockCommand).message,
225+
},
226+
Event: eventually.Event{
227+
Payload: mockCommandHasFailed{
228+
err: expectedErr,
229+
command: expectedCommand.Payload.(mockCommand),
230+
},
231+
},
232+
},
233+
}, trackingEventStore.Recorded())
234+
})
187235
}

0 commit comments

Comments
 (0)