From d60ff2c86acd1f5a5092ce89d2a28497572b26f7 Mon Sep 17 00:00:00 2001 From: Sonal Gupta Date: Thu, 12 May 2022 18:13:23 +0530 Subject: [PATCH 1/4] changes to get reacted message information --- README.md | 16 +++++++ client/slack.go | 38 +++++++++++++++++ client/slack_test.go | 69 +++++++++++++++++++++++++++++- command/reaction_test.go | 90 +++++++++++++++++++++++++++++++++++++++ command/reactions.go | 91 ++++++++++++++++++++++++++++++++++++++++ main.go | 1 + 6 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 command/reaction_test.go create mode 100644 command/reactions.go diff --git a/README.md b/README.md index e505843..67adb68 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,22 @@ The returned event payload is the same as the input. } ``` + +### GetReactionMessageInfo +This command would retrieve the message text of reaction sent by a user. + +``` +{ "count": 50 , //default value is 100 mandatory + "message": "" , + "threadTimestamp":"...", + "reactionUser":"...", // mandatory list of reactions for a user + "channelId": "...", + "threadTimestamp":"..." + } +``` + + + ## Events ### ReceivedMessage diff --git a/client/slack.go b/client/slack.go index e9cd41a..8588ae7 100644 --- a/client/slack.go +++ b/client/slack.go @@ -31,6 +31,7 @@ type client interface { SendMessage(message *slack.OutgoingMessage) PostMessage(channel string, opts ...slack.MsgOption) (string, string, error) GetConversations(params *slack.GetConversationsParameters) (channels []slack.Channel, nextCursor string, err error) + ListReactions(params slack.ListReactionsParameters) ([]slack.ReactedItem, *slack.Paging, error) } // our slack implementation makes consistent use of channel id @@ -41,6 +42,7 @@ type Slack interface { // GetConversations is a heavy call used to fetch data about all channels in a workspace // intended to be cached, not called each time this is needed GetConversations() ([]types.Conversation, error) + GetReactionMessageText(count int, user string, channelId, threadTimestamp string) (text string, err error) } type slackClient struct { @@ -208,3 +210,39 @@ func newUser(u *slack.User) user { LastName: u.Profile.LastName, } } + +func (sl *slackClient) GetReactionMessageText(count int, user string, channelId, threadTimestamp string) (text string, err error) { + params := slack.ListReactionsParameters{ + Count: count, + User: user, + } + log.Debug().Msgf("Count = %v user = %s channel id= %v threadTimestamp= %v", count, user, channelId, threadTimestamp) + + reaction, paging, err := sl.client.ListReactions(params) + if err != nil { + log.Debug().Msgf("Error = %v", err) + return "", err + } + + for i := range reaction { + if reaction[i].Type == "message" { + if reaction[i].Channel == channelId { + if reaction[i].Message.Timestamp == threadTimestamp { + log.Debug().Msgf("reaction match found added: Value of Type = %v , channel = %v Msg Timestamp = %v , Text = %v", + reaction[i].Type, reaction[i].Channel, reaction[i].Message.ThreadTimestamp, + reaction[i].Message.Text) + return reaction[i].Message.Text, nil + } else { + log.Debug().Msgf("reaction match not found with provided Timestamp") + } + + } else { + log.Debug().Msgf("reaction match not found with provided Channel") + } + } + } + + logger.Debugf("Value of paging = %v", paging) + return "", nil + +} diff --git a/client/slack_test.go b/client/slack_test.go index b54e8c8..57b3d3c 100644 --- a/client/slack_test.go +++ b/client/slack_test.go @@ -182,6 +182,64 @@ func TestIncomingMessages(t *testing.T) { } } + +func TestGetReactionMessageText(t *testing.T) { + + // given + Before(t) + + SlackMockClient.ListReactionsFunc = func(params slack.ListReactionsParameters) ([]slack.ReactedItem, *slack.Paging, error) { + + u := slack.Item{ + Type: "message", + Channel: "C030JSNMMT6", + Message: &slack.Message{ + Msg: slack.Msg{ + ClientMsgID: "6cff3493-f89d-4230-b89c-d2442c88983f", + Type: "message", + Channel: "C030JSNMMT6", + User: "U01NAB6ERFB", + Text: "test tickets", + Timestamp: "1652252021.476309", + ThreadTimestamp: "1652252021.476309", + IsStarred: false, + }, + }, + } + r := slack.ItemReaction{ + Name: "create-a-ticket", + Count: 1, + Users: []string{"U01NAB6ERFB"}, + } + + slackReactedItem := slack.ReactedItem{ + u, + []slack.ItemReaction{r}, + } + + return []slack.ReactedItem{slackReactedItem}, nil, nil + } + v, err := SlackImpl.GetReactionMessageText(50, "U01NAB6ERFB", "C030JSNMMT6", "1652252021.476309") + assert.Equal(t, "test tickets", v) + assert.Equal(t, nil, err) + +} + +func TestGetReactionMessageTextError(t *testing.T) { + + // given + Before(t) + + SlackMockClient.ListReactionsFunc = func(params slack.ListReactionsParameters) ([]slack.ReactedItem, *slack.Paging, error) { + + err := fmt.Errorf("some unknown error occurred") + return []slack.ReactedItem{}, nil, err + } + v, _ := SlackImpl.GetReactionMessageText(50, "U01NAB6ERFB", "C030JSNMMT6", "1652252021.476309") + assert.Equal(t, "", v) + +} + // --- helpers --- // this simulates messages coming from slack @@ -213,7 +271,12 @@ type MockClient struct { // map stores all the sent messages by channelId (key is channelId) OutgoingMessages map[string][]*slack.OutgoingMessage // Slice of rich messages - PostMessageFunc func(channel string, opts ...slack.MsgOption) (string, string, error) + PostMessageFunc func(channel string, opts ...slack.MsgOption) (string, string, error) + ListReactionsFunc func(params slack.ListReactionsParameters) ([]slack.ReactedItem, *slack.Paging, error) +} + +func (m *MockClient) ListReactions(params slack.ListReactionsParameters) ([]slack.ReactedItem, *slack.Paging, error) { + return m.ListReactionsFunc(params) } func NewMockClient(t *testing.T) *MockClient { @@ -224,6 +287,10 @@ func NewMockClient(t *testing.T) *MockClient { m.PostMessageFunc = func(channel string, params ...slack.MsgOption) (string, string, error) { return "", "", nil } + m.ListReactionsFunc = func(params slack.ListReactionsParameters) ([]slack.ReactedItem, *slack.Paging, error) { + return m.ListReactionsFunc(params) + } + return m } diff --git a/command/reaction_test.go b/command/reaction_test.go new file mode 100644 index 0000000..f29dd4c --- /dev/null +++ b/command/reaction_test.go @@ -0,0 +1,90 @@ +/* +Copyright (C) 2018 Expedia Group. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package command + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +var ReactionMockSlack *MockSlack + +func TestGetReactionListCommandIsPopulated(t *testing.T) { + + command := GetReactionMessageInfo(ReactionMockSlack) + + assert.Equal(t, "GetReactionMessageInfo", command.Name) + require.Equal(t, 2, len(command.OutputEvents)) + assert.Equal(t, "GetReactionMessageInfoSuccess", command.OutputEvents[0].Name) + assert.Equal(t, "GetReactionMessageInfoFailed", command.OutputEvents[1].Name) +} + +func TestGetReactionListReturnsGetReactionListSuccess(t *testing.T) { + + BeforeMessage() + defer AfterMessage() + + handler := GetReactionMessageInfo(ReactionMockSlack).Handler + event := handler([]byte(`{"count": 50 , + "message": "" , + "threadTimestamp":"1645441176.871569", + "reactionUser":"UXXXXXX", + "channelId": "CXXXXXXX", + "threadTimestamp":"1645441176.871569" + }`)) + output := event.Payload.(GetReactionMessageInfoOutput) + assert.Equal(t, "GetReactionMessageInfoSuccess", event.EventDef.Name) + assert.Equal(t, "", output.Message) + assert.Equal(t, "CXXXXXXX", output.ChannelId) +} + +func TestGetReactionListReturnsGetReactionMessageInfoFailedMissingTimestamp(t *testing.T) { + + BeforeMessage() + defer AfterMessage() + + handler := GetReactionMessageInfo(ReactionMockSlack).Handler + event := handler([]byte(`{"count": 50 , + "message": "" , + "reactionUser":"UXXXXXX", + "channelId": "CXXXXXXX", + "threadTimestamp":"" + }`)) + output := event.Payload.(GetReactionMessageInfoFailed) + output = output + assert.Equal(t, "GetReactionMessageInfoFailed", event.EventDef.Name) + assert.Equal(t, "missing Message Timestamp field", output.Error) +} + +func TestGetReactionListReturnsGetReactionMessageInfoFailedMissingReactionUser(t *testing.T) { + + BeforeMessage() + defer AfterMessage() + + handler := GetReactionMessageInfo(ReactionMockSlack).Handler + event := handler([]byte(`{"count": 50 , + "message": "" , + "reactionUser":"", + "channelId": "CXXXXXXX", + "threadTimestamp":"213.4445" + }`)) + output := event.Payload.(GetReactionMessageInfoFailed) + output = output + assert.Equal(t, "GetReactionMessageInfoFailed", event.EventDef.Name) + assert.Equal(t, "missing user id field", output.Error) +} diff --git a/command/reactions.go b/command/reactions.go new file mode 100644 index 0000000..f91d5e2 --- /dev/null +++ b/command/reactions.go @@ -0,0 +1,91 @@ +package command + +import ( + "encoding/json" + "fmt" + "github.com/ExpediaGroup/flyte-slack/client" + "github.com/HotelsDotCom/flyte-client/flyte" + "strings" +) + +var ( + getReactionMessageInfoEventDef = flyte.EventDef{Name: "GetReactionMessageInfoSuccess"} + getReactionMessageInfoFailedEventDef = flyte.EventDef{Name: "GetReactionMessageInfoFailed"} +) + +type GetReactionMessageInfoInput struct { + Count int `json:"count"` + Message string `json:"message"` + ThreadTimestamp string `json:"threadTimestamp"` + User string `json:"reactionUser"` + ChannelId string `json:"channelId"` + ItemUser string `json:"itemUser"` +} + +type GetReactionMessageInfoOutput struct { + GetReactionMessageInfoInput +} + +type GetReactionMessageInfoFailed struct { + GetReactionMessageInfoOutput + Error string `json:"error"` +} + +func GetReactionMessageInfo(slack client.Slack) flyte.Command { + + return flyte.Command{ + Name: "GetReactionMessageInfo", + OutputEvents: []flyte.EventDef{getReactionMessageInfoEventDef, getReactionMessageInfoFailedEventDef}, + Handler: getReactionMessageInfoHandler(slack), + } +} + +func getReactionMessageInfoHandler(slack client.Slack) func(json.RawMessage) flyte.Event { + + return func(rawInput json.RawMessage) flyte.Event { + input := GetReactionMessageInfoInput{} + if err := json.Unmarshal(rawInput, &input); err != nil { + return flyte.NewFatalEvent(fmt.Sprintf("input is not valid: %v", err)) + } + + errorMessages := []string{} + if input.ThreadTimestamp == "" { + errorMessages = append(errorMessages, "missing Message Timestamp field") + } + if input.ChannelId == "" { + errorMessages = append(errorMessages, "missing channel id field") + } + if input.User == "" { + errorMessages = append(errorMessages, "missing user id field") + } + if len(errorMessages) != 0 { + return getReactionMessageFailedEvent(input.Message, input.ChannelId, strings.Join(errorMessages, ", ")) + } + + issueSummary, err := slack.GetReactionMessageText(input.Count, input.User, input.ChannelId, input.ThreadTimestamp) + if err != nil { + resp := fmt.Errorf("Got the error = [%s] while listing the reaction for user %s Channel= %s ", err, input.User, input.ChannelId) + errorMessages = append(errorMessages, fmt.Sprintf("input is not valid: %v", resp)) + return getReactionMessageFailedEvent(input.Message, input.ChannelId, strings.Join(errorMessages, ", ")) + } + + return getReactionMessageSuccessInfoEvent(issueSummary, input.ChannelId) + } +} + +func getReactionMessageSuccessInfoEvent(message, channelId string) flyte.Event { + + return flyte.Event{ + EventDef: getReactionMessageInfoEventDef, + Payload: GetReactionMessageInfoOutput{GetReactionMessageInfoInput: GetReactionMessageInfoInput{Message: message, ChannelId: channelId}}, + } +} + +func getReactionMessageFailedEvent(message, channelId string, err string) flyte.Event { + + output := GetReactionMessageInfoOutput{GetReactionMessageInfoInput{Message: message, ChannelId: channelId}} + return flyte.Event{ + EventDef: getReactionMessageInfoFailedEventDef, + Payload: GetReactionMessageInfoFailed{GetReactionMessageInfoOutput: output, Error: err}, + } +} diff --git a/main.go b/main.go index 64d7ce5..3a0d643 100644 --- a/main.go +++ b/main.go @@ -56,6 +56,7 @@ func packDef(slack client.Slack, cache cache.Cache) flyte.PackDef { command.SendMessage(slack), command.SendRichMessage(slack), command.GetChannelInfo(slack, cache), + command.GetReactionMessageInfo(slack), }, EventDefs: []flyte.EventDef{ {Name: "ReceivedMessage"}, From cf6f9f85d4a9302b665167ce9295237e9d78f8e0 Mon Sep 17 00:00:00 2001 From: Sonal Gupta Date: Thu, 12 May 2022 18:39:00 +0530 Subject: [PATCH 2/4] changes - replaced the logger library with zerolog --- client/slack.go | 4 ++-- command/reaction_test.go | 3 --- command/reactions.go | 2 +- command/util_test.go | 10 ++++++++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/client/slack.go b/client/slack.go index 8588ae7..c0351b3 100644 --- a/client/slack.go +++ b/client/slack.go @@ -71,7 +71,7 @@ func NewSlack(token string) Slack { const ( getConversationsLimit = 1000 // max 1000 - excludeArchived = true + excludeArchived = true ) func (sl *slackClient) GetConversations() ([]types.Conversation, error) { @@ -242,7 +242,7 @@ func (sl *slackClient) GetReactionMessageText(count int, user string, channelId, } } - logger.Debugf("Value of paging = %v", paging) + log.Debug().Msgf("Value of paging = %v", paging) return "", nil } diff --git a/command/reaction_test.go b/command/reaction_test.go index f29dd4c..c170b52 100644 --- a/command/reaction_test.go +++ b/command/reaction_test.go @@ -37,7 +37,6 @@ func TestGetReactionListCommandIsPopulated(t *testing.T) { func TestGetReactionListReturnsGetReactionListSuccess(t *testing.T) { BeforeMessage() - defer AfterMessage() handler := GetReactionMessageInfo(ReactionMockSlack).Handler event := handler([]byte(`{"count": 50 , @@ -56,7 +55,6 @@ func TestGetReactionListReturnsGetReactionListSuccess(t *testing.T) { func TestGetReactionListReturnsGetReactionMessageInfoFailedMissingTimestamp(t *testing.T) { BeforeMessage() - defer AfterMessage() handler := GetReactionMessageInfo(ReactionMockSlack).Handler event := handler([]byte(`{"count": 50 , @@ -74,7 +72,6 @@ func TestGetReactionListReturnsGetReactionMessageInfoFailedMissingTimestamp(t *t func TestGetReactionListReturnsGetReactionMessageInfoFailedMissingReactionUser(t *testing.T) { BeforeMessage() - defer AfterMessage() handler := GetReactionMessageInfo(ReactionMockSlack).Handler event := handler([]byte(`{"count": 50 , diff --git a/command/reactions.go b/command/reactions.go index f91d5e2..7746e39 100644 --- a/command/reactions.go +++ b/command/reactions.go @@ -3,8 +3,8 @@ package command import ( "encoding/json" "fmt" + "github.com/ExpediaGroup/flyte-client/flyte" "github.com/ExpediaGroup/flyte-slack/client" - "github.com/HotelsDotCom/flyte-client/flyte" "strings" ) diff --git a/command/util_test.go b/command/util_test.go index eafadc9..fcc8caa 100644 --- a/command/util_test.go +++ b/command/util_test.go @@ -49,3 +49,13 @@ func (m *MockSlack) IncomingMessages() <-chan flyte.Event { func (m *MockSlack) GetConversations() ([]types.Conversation, error) { return []types.Conversation(nil), nil } + +func (m *MockSlack) GetReactionMessageText(count int, user string, channelId, threadTimestamp string) (text string, err error) { + count = 50 + channelId = "CXXXXX" + threadTimestamp = "765523.455667" + if user == "UXXXXX" { + return "This is sample test reaction message for user", nil + } + return "", nil +} From f7c31fa7fee0e509fdbf8ce570d10a0f9bfd3b30 Mon Sep 17 00:00:00 2001 From: Sonal Gupta Date: Thu, 12 May 2022 20:03:17 +0530 Subject: [PATCH 3/4] added changes --- client/slack.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/client/slack.go b/client/slack.go index c0351b3..eab5bf2 100644 --- a/client/slack.go +++ b/client/slack.go @@ -218,7 +218,7 @@ func (sl *slackClient) GetReactionMessageText(count int, user string, channelId, } log.Debug().Msgf("Count = %v user = %s channel id= %v threadTimestamp= %v", count, user, channelId, threadTimestamp) - reaction, paging, err := sl.client.ListReactions(params) + reaction, _, err := sl.client.ListReactions(params) if err != nil { log.Debug().Msgf("Error = %v", err) return "", err @@ -233,16 +233,15 @@ func (sl *slackClient) GetReactionMessageText(count int, user string, channelId, reaction[i].Message.Text) return reaction[i].Message.Text, nil } else { - log.Debug().Msgf("reaction match not found with provided Timestamp") + err := fmt.Errorf("match not found for input timestamp") + return "", err } } else { - log.Debug().Msgf("reaction match not found with provided Channel") + err := fmt.Errorf("Reaction match not found for input channel") + return "", err } } } - - log.Debug().Msgf("Value of paging = %v", paging) return "", nil - } From ca50b56bb4d867ecde5eb516c2cb1d0bad34fdd5 Mon Sep 17 00:00:00 2001 From: Sonal Gupta Date: Thu, 12 May 2022 20:06:50 +0530 Subject: [PATCH 4/4] added changes --- client/slack.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/client/slack.go b/client/slack.go index eab5bf2..4fa1e20 100644 --- a/client/slack.go +++ b/client/slack.go @@ -233,15 +233,13 @@ func (sl *slackClient) GetReactionMessageText(count int, user string, channelId, reaction[i].Message.Text) return reaction[i].Message.Text, nil } else { - err := fmt.Errorf("match not found for input timestamp") - return "", err + log.Debug().Msgf("reaction match not found with provided Timestamp") } - } else { - err := fmt.Errorf("Reaction match not found for input channel") - return "", err + log.Debug().Msgf("reaction match not found with provided Channel") } } } - return "", nil + err = fmt.Errorf("message info not found for input timestamp and channel") + return "", err }