diff --git a/pkg/custmsg/custom_message.go b/pkg/custmsg/custom_message.go index a2f9b7d2ad..14915fa058 100644 --- a/pkg/custmsg/custom_message.go +++ b/pkg/custmsg/custom_message.go @@ -11,6 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/beholder/pb" ) +const LabelKeyType = "type" + type MessageEmitter interface { // Emit sends a message to the labeler's destination. Emit(context.Context, string) error @@ -18,6 +20,13 @@ type MessageEmitter interface { // WithMapLabels sets the labels for the message to be emitted. Labels are cumulative. WithMapLabels(map[string]string) MessageEmitter + // WithType sets the required message type label (BaseMessage.Labels["type"]). + WithType(msgType string) MessageEmitter + + // WithLabelsAndType sets labels and the required message type label. + // msgType wins over any "type" key already present in labels. + WithLabelsAndType(labels map[string]string, msgType string) MessageEmitter + // With adds multiple key-value pairs to the emission. With(keyValues ...string) MessageEmitter @@ -47,6 +56,22 @@ func (l Labeler) WithMapLabels(labels map[string]string) MessageEmitter { return newCustomMessageLabeler } +func (l Labeler) WithType(msgType string) MessageEmitter { + newCustomMessageLabeler := NewLabeler() + maps.Copy(newCustomMessageLabeler.labels, l.labels) + newCustomMessageLabeler.labels[LabelKeyType] = msgType + return newCustomMessageLabeler +} + +func (l Labeler) WithLabelsAndType(labels map[string]string, msgType string) MessageEmitter { + newCustomMessageLabeler := NewLabeler() + maps.Copy(newCustomMessageLabeler.labels, l.labels) + maps.Copy(newCustomMessageLabeler.labels, labels) + newCustomMessageLabeler.labels[LabelKeyType] = msgType + return newCustomMessageLabeler +} + + // With adds multiple key-value pairs to the CustomMessageLabeler for transmission With SendLogAsCustomMessage func (l Labeler) With(keyValues ...string) MessageEmitter { newCustomMessageLabeler := NewLabeler() @@ -88,6 +113,10 @@ func (l Labeler) SendLogAsCustomMessage(ctx context.Context, msg string) error { } func sendLogAsCustomMessageW(ctx context.Context, msg string, labels map[string]string) error { + if labels[LabelKeyType] == "" { + return fmt.Errorf("custmsg: missing required label %q", LabelKeyType) + } + // TODO un-comment after INFOPLAT-1386 // cast to map[string]any //newLabels := map[string]any{} diff --git a/pkg/custmsg/custom_message_test.go b/pkg/custmsg/custom_message_test.go index a158c1a57a..f951cf9c46 100644 --- a/pkg/custmsg/custom_message_test.go +++ b/pkg/custmsg/custom_message_test.go @@ -1,26 +1,49 @@ package custmsg import ( + "context" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // tests CustomMessageAgent does not share state across new instances created by `With` func Test_CustomMessageAgent(t *testing.T) { cma := NewLabeler() - cma1 := cma.With("key1", "value1") + cma1 := cma.With("key1", "value1").WithType("TestType") cma2 := cma1.With("key2", "value2") assert.NotEqual(t, cma1.Labels(), cma2.Labels()) } func Test_CustomMessageAgent_With(t *testing.T) { - cma := NewLabeler().With("key1", "value1") - assert.Equal(t, map[string]string{"key1": "value1"}, cma.Labels()) + cma := NewLabeler().WithType("TestType").With("key1", "value1") + assert.Equal(t, map[string]string{"key1": "value1", LabelKeyType: "TestType"}, cma.Labels()) } func Test_CustomMessageAgent_WithMapLabels(t *testing.T) { - cma := NewLabeler().WithMapLabels(map[string]string{"key1": "value1"}) - assert.Equal(t, map[string]string{"key1": "value1"}, cma.Labels()) + cma := NewLabeler().WithType("TestType").WithMapLabels(map[string]string{"key1": "value1"}) + assert.Equal(t, map[string]string{"key1": "value1", LabelKeyType: "TestType"}, cma.Labels()) +} + +func Test_CustomMessageAgent_WithType(t *testing.T) { + cma := NewLabeler().WithType("NodeConfig") + assert.Equal(t, map[string]string{LabelKeyType: "NodeConfig"}, cma.Labels()) +} + +func Test_CustomMessageAgent_WithLabelsAndType(t *testing.T) { + cma := NewLabeler().WithLabelsAndType(map[string]string{ + "system": "Application", + "type": "ignored", + }, "NodeConfig") + assert.Equal(t, map[string]string{ + "system": "Application", + LabelKeyType: "NodeConfig", + }, cma.Labels()) +} + +func Test_CustomMessageAgent_EmitRequiresType(t *testing.T) { + err := NewLabeler().Emit(context.Background(), "msg") + require.ErrorContains(t, err, `missing required label "type"`) } diff --git a/pkg/custmsg/mock_message_emitter_test.go b/pkg/custmsg/mock_message_emitter_test.go index 5ef3ad6652..6df00a1649 100644 --- a/pkg/custmsg/mock_message_emitter_test.go +++ b/pkg/custmsg/mock_message_emitter_test.go @@ -224,6 +224,44 @@ func (_c *mockMessageEmitter_WithMapLabels_Call) RunAndReturn(run func(map[strin return _c } +func (_m *mockMessageEmitter) WithType(_a0 string) MessageEmitter { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for WithType") + } + + var r0 MessageEmitter + if rf, ok := ret.Get(0).(func(string) MessageEmitter); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(MessageEmitter) + } + } + + return r0 +} + +func (_m *mockMessageEmitter) WithLabelsAndType(_a0 map[string]string, _a1 string) MessageEmitter { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for WithLabelsAndType") + } + + var r0 MessageEmitter + if rf, ok := ret.Get(0).(func(map[string]string, string) MessageEmitter); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(MessageEmitter) + } + } + + return r0 +} + // newMockMessageEmitter creates a new instance of mockMessageEmitter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func newMockMessageEmitter(t interface { diff --git a/pkg/workflows/wasm/host/module.go b/pkg/workflows/wasm/host/module.go index 2f64dae3da..e9cd2163d6 100644 --- a/pkg/workflows/wasm/host/module.go +++ b/pkg/workflows/wasm/host/module.go @@ -1062,6 +1062,14 @@ func (u *unimplementedMessageEmitter) WithMapLabels(map[string]string) custmsg.M return u } +func (u *unimplementedMessageEmitter) WithType(string) custmsg.MessageEmitter { + return u +} + +func (u *unimplementedMessageEmitter) WithLabelsAndType(map[string]string, string) custmsg.MessageEmitter { + return u +} + func (u *unimplementedMessageEmitter) With(kvs ...string) custmsg.MessageEmitter { return u } diff --git a/pkg/workflows/wasm/host/module_test.go b/pkg/workflows/wasm/host/module_test.go index bfa1818576..e664a0d9f7 100644 --- a/pkg/workflows/wasm/host/module_test.go +++ b/pkg/workflows/wasm/host/module_test.go @@ -39,6 +39,23 @@ func (m *mockMessageEmitter) WithMapLabels(labels map[string]string) custmsg.Mes return m } +func (m *mockMessageEmitter) WithType(msgType string) custmsg.MessageEmitter { + if m.labels == nil { + m.labels = map[string]string{} + } + m.labels[custmsg.LabelKeyType] = msgType + return m +} + +func (m *mockMessageEmitter) WithLabelsAndType(labels map[string]string, msgType string) custmsg.MessageEmitter { + m.labels = labels + if m.labels == nil { + m.labels = map[string]string{} + } + m.labels[custmsg.LabelKeyType] = msgType + return m +} + func (m *mockMessageEmitter) With(keyValues ...string) custmsg.MessageEmitter { // do nothing return m