From cd6747e846146c38b7f0d07eae58a8fedc7fd273 Mon Sep 17 00:00:00 2001 From: Mateusz Szostok Date: Mon, 4 Jul 2022 17:49:21 +0200 Subject: [PATCH 1/2] Add option to detect unmapped error --- websocket_managed_conn.go | 34 ++++++++++++++++++- websocket_managed_conn_test.go | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/websocket_managed_conn.go b/websocket_managed_conn.go index 14dea2196..249450bb0 100644 --- a/websocket_managed_conn.go +++ b/websocket_managed_conn.go @@ -16,6 +16,38 @@ import ( "github.com/slack-go/slack/internal/timex" ) +// UnmappedError represents error occurred when there is no mapping between given event name +// and corresponding Go struct. +type UnmappedError struct { + eventType string + rawEvent json.RawMessage + ctxMsg string +} + +// NewUnmappedError returns new UnmappedError instance. +func NewUnmappedError(ctxMsg, eventType string, raw json.RawMessage) *UnmappedError { + return &UnmappedError{ + ctxMsg: ctxMsg, + eventType: eventType, + rawEvent: raw, + } +} + +// Error returns human-readable error message. +func (u UnmappedError) Error() string { + return fmt.Sprintf("%s: Received unmapped event %q", u.ctxMsg, u.eventType) +} + +// EventType returns event type name. +func (u UnmappedError) EventType() string { + return u.eventType +} + +// RawEvent returns raw event body. +func (u UnmappedError) RawEvent() json.RawMessage { + return u.rawEvent +} + // ManageConnection can be called on a Slack RTM instance returned by the // NewRTM method. It will connect to the slack RTM API and handle all incoming // and outgoing events. If a connection fails then it will attempt to reconnect @@ -474,7 +506,7 @@ func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) { v, exists := EventMapping[typeStr] if !exists { rtm.Debugf("RTM Error - received unmapped event %q: %s\n", typeStr, string(event)) - err := fmt.Errorf("RTM Error: Received unmapped event %q", typeStr) + err := NewUnmappedError("RTM Error", typeStr, event) rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}} return } diff --git a/websocket_managed_conn_test.go b/websocket_managed_conn_test.go index 429ce8d52..0e899aa44 100644 --- a/websocket_managed_conn_test.go +++ b/websocket_managed_conn_test.go @@ -1,6 +1,7 @@ package slack_test import ( + "encoding/json" "fmt" "log" "net/http" @@ -9,6 +10,7 @@ import ( websocket "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/slack-go/slack" "github.com/slack-go/slack/slacktest" @@ -313,3 +315,61 @@ func TestRTMSingleConnect(t *testing.T) { assert.True(t, connectedReceived, "Should have received a connected event from the RTM instance.") assert.True(t, testMessageReceived, "Should have received a test message from the server.") } + +func TestRTMUnmappedError(t *testing.T) { + const unmappedEventName = "user_status_changed" + // Set up the test server. + testServer := slacktest.NewTestServer() + go testServer.Start() + + // Setup and start the RTM. + api := slack.New(testToken, slack.OptionAPIURL(testServer.GetAPIURL())) + rtm := api.NewRTM() + go rtm.ManageConnection() + + // Observe incoming messages. + done := make(chan struct{}) + var gotUnmarshallingError *slack.UnmarshallingErrorEvent + go func() { + for msg := range rtm.IncomingEvents { + switch ev := msg.Data.(type) { + case *slack.UnmarshallingErrorEvent: + gotUnmarshallingError = ev + rtm.Disconnect() + case *slack.DisconnectedEvent: + if ev.Intentional { + done <- struct{}{} + return + } + default: + t.Logf("Discarded event of type '%s' with content '%#v'", msg.Type, ev) + } + } + }() + + // Send a message and sleep for some time to make sure the message can be processed client-side. + testServer.SendToWebsocket(fixSlackMessage(t, unmappedEventName)) + <-done + testServer.Stop() + + // Verify that we got the expected error with details + unmappedErr, ok := gotUnmarshallingError.ErrorObj.(*slack.UnmappedError) + require.True(t, ok) + assert.Equal(t, unmappedEventName, unmappedErr.EventType()) +} + +func fixSlackMessage(t *testing.T, eType string) string { + t.Helper() + + m := slack.Message{ + Msg: slack.Msg{ + Type: eType, + Text: "Fixture Slack message", + Timestamp: fmt.Sprintf("%d", time.Now().Unix()), + }, + } + msg, err := json.Marshal(m) + require.NoError(t, err) + + return string(msg) +} From 9e4de8bcdbb3ebe4789c18015ecb9b6bf99a66eb Mon Sep 17 00:00:00 2001 From: Mateusz Szostok Date: Thu, 7 Jul 2022 00:11:21 +0200 Subject: [PATCH 2/2] Apply suggestion after review --- websocket_managed_conn.go | 25 +++++++++---------------- websocket_managed_conn_test.go | 2 +- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/websocket_managed_conn.go b/websocket_managed_conn.go index 249450bb0..f107b2a47 100644 --- a/websocket_managed_conn.go +++ b/websocket_managed_conn.go @@ -19,33 +19,26 @@ import ( // UnmappedError represents error occurred when there is no mapping between given event name // and corresponding Go struct. type UnmappedError struct { - eventType string - rawEvent json.RawMessage - ctxMsg string + // EventType returns event type name. + EventType string + // RawEvent returns raw event body. + RawEvent json.RawMessage + + ctxMsg string } // NewUnmappedError returns new UnmappedError instance. func NewUnmappedError(ctxMsg, eventType string, raw json.RawMessage) *UnmappedError { return &UnmappedError{ ctxMsg: ctxMsg, - eventType: eventType, - rawEvent: raw, + EventType: eventType, + RawEvent: raw, } } // Error returns human-readable error message. func (u UnmappedError) Error() string { - return fmt.Sprintf("%s: Received unmapped event %q", u.ctxMsg, u.eventType) -} - -// EventType returns event type name. -func (u UnmappedError) EventType() string { - return u.eventType -} - -// RawEvent returns raw event body. -func (u UnmappedError) RawEvent() json.RawMessage { - return u.rawEvent + return fmt.Sprintf("%s: Received unmapped event %q", u.ctxMsg, u.EventType) } // ManageConnection can be called on a Slack RTM instance returned by the diff --git a/websocket_managed_conn_test.go b/websocket_managed_conn_test.go index 0e899aa44..c7a8b7034 100644 --- a/websocket_managed_conn_test.go +++ b/websocket_managed_conn_test.go @@ -355,7 +355,7 @@ func TestRTMUnmappedError(t *testing.T) { // Verify that we got the expected error with details unmappedErr, ok := gotUnmarshallingError.ErrorObj.(*slack.UnmappedError) require.True(t, ok) - assert.Equal(t, unmappedEventName, unmappedErr.EventType()) + assert.Equal(t, unmappedEventName, unmappedErr.EventType) } func fixSlackMessage(t *testing.T, eType string) string {