From a117473578291e777111cd565b68cc4b97a1d081 Mon Sep 17 00:00:00 2001 From: Danilo Cianfrone Date: Mon, 30 Aug 2021 11:07:52 +0200 Subject: [PATCH] feat(command): add StreamNameMapper to ErrorRecorder --- command/error_recorder.go | 13 ++++++++- command/error_recorder_test.go | 50 +++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/command/error_recorder.go b/command/error_recorder.go index 1fb89cb7..68cbc914 100644 --- a/command/error_recorder.go +++ b/command/error_recorder.go @@ -37,6 +37,12 @@ type ErrorRecorder struct { // If unspecified, FailedCommandType will be used by default. StreamType string + // StreamNameMapper maps to a StreamName value based on the command that failed. + // This is useful in case you want to use the same Aggregate id the command is targeting. + // + // If unspecified, the command name will be used instead. + StreamNameMapper func(cmd eventually.Command) string + // EventMapper should return the Domain Event type you defined for these commands. // // NOTE: this is necessary for (de)-serialization purposes while generics are @@ -53,9 +59,14 @@ func (er ErrorRecorder) streamType() string { } func (er ErrorRecorder) buildStreamID(cmd eventually.Command) eventstore.StreamID { + streamName := cmd.Payload.Name() + if er.StreamNameMapper != nil { + streamName = er.StreamNameMapper(cmd) + } + return eventstore.StreamID{ Type: er.streamType(), - Name: cmd.Payload.Name(), + Name: streamName, } } diff --git a/command/error_recorder_test.go b/command/error_recorder_test.go index ea5ac85e..acb27965 100644 --- a/command/error_recorder_test.go +++ b/command/error_recorder_test.go @@ -145,7 +145,8 @@ func TestErrorRecorder(t *testing.T) { eventStore := inmemory.NewEventStore() trackingEventStore := inmemory.NewTrackingEventStore(eventStore) - expectedStreamType := "mocks-command" + const expectedStreamType = "mocks-command" + expectedErr := errors.New("failed command") expectedCommand := eventually.Command{ Payload: mockCommand{message: t.Name()}, @@ -184,4 +185,51 @@ func TestErrorRecorder(t *testing.T) { }, }, trackingEventStore.Recorded()) }) + + t.Run("when handler fails, record event with custom stream name", func(t *testing.T) { + eventStore := inmemory.NewEventStore() + trackingEventStore := inmemory.NewTrackingEventStore(eventStore) + + expectedStreamType := "mocks-command" + expectedErr := errors.New("failed command") + expectedCommand := eventually.Command{ + Payload: mockCommand{message: t.Name()}, + } + + handler := command.ErrorRecorder{ + Handler: command.HandlerFunc(func(ctx context.Context, cmd eventually.Command) error { + return expectedErr + }), + Appender: trackingEventStore, + StreamType: expectedStreamType, + StreamNameMapper: func(cmd eventually.Command) string { + return cmd.Payload.(mockCommand).message + }, + EventMapper: func(err error, cmd eventually.Command) eventually.Payload { + return mockCommandHasFailed{ + err: err, + command: cmd.Payload.(mockCommand), + } + }, + } + + err := handler.Handle(context.Background(), expectedCommand) + + assert.Error(t, err) + assert.Equal(t, []eventstore.Event{ + { + Version: 1, + Stream: eventstore.StreamID{ + Type: expectedStreamType, + Name: expectedCommand.Payload.(mockCommand).message, + }, + Event: eventually.Event{ + Payload: mockCommandHasFailed{ + err: expectedErr, + command: expectedCommand.Payload.(mockCommand), + }, + }, + }, + }, trackingEventStore.Recorded()) + }) }