Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/get all reacted users #18

Merged
merged 14 commits into from
Mar 21, 2023
15 changes: 15 additions & 0 deletions app/internal/domain/repository/mock/slack.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions app/internal/domain/repository/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,19 @@ import (
)

type SlackRepository interface {
// PostMessage send a message to a channel
PostMessage(ctx context.Context, channelID, message, ts string) error

// PostEphemeral sends an ephemeral message to user in a channel
PostEphemeral(ctx context.Context, channelID, message, ts, userID string) error

// GetParentMessage gets Slack message that started the thread
GetParentMessage(ctx context.Context, channelID, ts string) (*model.SlackMessage, error)

// ListUsersEmail fetches users email
ListUsersEmail(ctx context.Context, userID []string) ([]*model.SlackUserEmail, error)

// GetReactions fetches reactions
// full: if true always return the complete reaction list
GetReactions(ctx context.Context, channelID, ts string, full bool) ([]*model.SlackReaction, error)
}
27 changes: 24 additions & 3 deletions app/internal/domain/service/slack_reaction_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,35 @@ func (s *slackReactionUsersService) ListUsersEmailByReaction(ctx context.Context
if err != nil {
return nil, err
}
inviteUserIDs := s.getReactionUserIDs(ctx, msg.Reactions, reactionName)
inviteUserEmails, err := s.chunkedListUsersEmail(ctx, inviteUserIDs)
if s.isReactionRefetchNeeded(msg.Reactions) {
msg.Reactions, err = s.getFullReactions(ctx, channelID, ts)
if err != nil {
return nil, err
}
}
reactedUserIDs := s.getReactionUserIDs(ctx, msg.Reactions, reactionName)
reactedUserEmails, err := s.chunkedListUsersEmail(ctx, reactedUserIDs)
if err != nil {
return nil, err
}
return inviteUserEmails, nil
return reactedUserEmails, nil
}

// isReactionRefetchNeeded returns true if more fetches is required
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どういう時に refetch が必要なのか、軽くコメント入れておいてくれると助かる。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとう!入れておきました
add comment

func (s *slackReactionUsersService) isReactionRefetchNeeded(reactions []*model.SlackReaction) bool {
for _, r := range reactions {
if r.Count != len(r.UserIDs) {
return true
}
}
return false
}

// getReactionUserIDs get reaction users by reactionName
func (s *slackReactionUsersService) getReactionUserIDs(ctx context.Context, reactions []*model.SlackReaction, reactionName string) []string {
var userIDs []string
var targetReactions []*model.SlackReaction
// filtered reactions by reactionName
for _, reaction := range reactions {
rn := slack.ExtractReactionName(reaction.Name)
if slack.RemoveSkinToneFromReaction(rn) == reactionName {
Expand All @@ -94,3 +111,7 @@ func (s *slackReactionUsersService) getReactionUserIDs(ctx context.Context, reac
}
return slice.ToStringSet(userIDs)
}

func (s *slackReactionUsersService) getFullReactions(ctx context.Context, channelID, ts string) ([]*model.SlackReaction, error) {
return s.slackRepository.GetReactions(ctx, channelID, ts, true)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この処理 func に切り出すほどの処理だった?

Copy link
Member Author

@sho0126hiro sho0126hiro Mar 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trueを直で引数に指定するのが、マジックナンバーみたいに見えるから関数に切り出した感じ(意味がわかるように)。
どっちでも良いとは思ってるけどどうでしょう?

Copy link
Member Author

@sho0126hiro sho0126hiro Mar 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここでture直書きにしておきました
fix implementation

83 changes: 83 additions & 0 deletions app/internal/domain/service/slack_reaction_users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ func Test_slackReactionUsersService_ListUsersEmailByReaction(t *testing.T) {
{
Name: "join",
UserIDs: []string{"user01", "user02"},
Count: 2,
},
{
Name: "reactionSample",
UserIDs: []string{"user02", "user03"},
Count: 2,
},
},
}
Expand Down Expand Up @@ -76,6 +78,58 @@ func Test_slackReactionUsersService_ListUsersEmailByReaction(t *testing.T) {
{ID: "user03", Email: "[email protected]"},
},
},
{
name: "OK: There is difference between len(Reaction.UserIDs) and Reaction.Count",
args: args{
channelID: "sampleCID", ts: "sampleTs", reactionName: "reactionSample",
},
prepare: func(msr *mock_repository.MockSlackRepository) {
gomock.InOrder(
msr.EXPECT().GetParentMessage(gomock.Any(), "sampleCID", "sampleTs").Return(
&model.SlackMessage{
ChannelID: "sampleCID",
Reactions: []*model.SlackReaction{
{
Name: "join",
UserIDs: []string{"user01", "user02"},
Count: 2,
},
{
Name: "reactionSample",
UserIDs: []string{"user02", "user03"},
Count: 4, // number of counts that need to be re-fetched
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これって len(UserIDs) > Count のパターンってありえないんだっけ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありえないと思う。APIドキュメントには明示されていなかったけど、実データとして確認しているのは
len(UserIDs) =< Count の時だけかな。
判定部分が != になっていたから、判定部を修正しておきました

fix condition

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 fix implementationisIncompleteReactionにロジックを移したので、ここのコメントと判定部を修正しておきました

},
},
}, nil),
msr.EXPECT().GetReactions(gomock.Any(), "sampleCID", "sampleTs", true).Return(
[]*model.SlackReaction{
{
Name: "join",
UserIDs: []string{"user01", "user02"},
Count: 2,
},
{
Name: "reactionSample",
UserIDs: []string{"user02", "user03", "user04", "user05"}, // fully fetched
Count: 4,
},
}, nil),
msr.EXPECT().ListUsersEmail(gomock.Any(), []string{"user02", "user03", "user04", "user05"}).Return(
[]*model.SlackUserEmail{
{ID: "user02", Email: "[email protected]"},
{ID: "user03", Email: "[email protected]"},
{ID: "user04", Email: "[email protected]"},
{ID: "user05", Email: "[email protected]"},
}, nil),
)
},
want: []*model.SlackUserEmail{
{ID: "user02", Email: "[email protected]"},
{ID: "user03", Email: "[email protected]"},
{ID: "user04", Email: "[email protected]"},
{ID: "user05", Email: "[email protected]"},
},
},
{
name: "NG: error in GetParentMessage",
args: args{
Expand Down Expand Up @@ -104,6 +158,35 @@ func Test_slackReactionUsersService_ListUsersEmailByReaction(t *testing.T) {
},
wantErr: true,
},
{
name: "NG: error in GetReactions",
args: args{
channelID: "sampleCID", ts: "sampleTs", reactionName: "reactionSample",
},
prepare: func(msr *mock_repository.MockSlackRepository) {
gomock.InOrder(
msr.EXPECT().GetParentMessage(gomock.Any(), "sampleCID", "sampleTs").Return(
&model.SlackMessage{
ChannelID: "sampleCID",
Reactions: []*model.SlackReaction{
{
Name: "join",
UserIDs: []string{"user01", "user02"},
Count: 2,
},
{
Name: "reactionSample",
UserIDs: []string{"user02", "user03"},
Count: 4, // number of counts that need to be re-fetched
},
},
}, nil),
msr.EXPECT().GetReactions(gomock.Any(), "sampleCID", "sampleTs", true).Return(
nil, errors.New("sample_error")),
)
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions app/internal/model/slack_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type SlackMessage struct {

type SlackReaction struct {
Name string
Count int
UserIDs []string
}

Expand Down
17 changes: 17 additions & 0 deletions app/internal/repository/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func (r *slackRepository) GetParentMessage(ctx context.Context, channelID, ts st
reactions = append(reactions, &model.SlackReaction{
Name: reaction.Name,
UserIDs: reaction.Users,
Count: reaction.Count,
})
}
return &model.SlackMessage{
Expand Down Expand Up @@ -93,3 +94,19 @@ func (r *slackRepository) ListUsersEmail(ctx context.Context, userID []string) (
}
return slackUsers, nil
}

func (r *slackRepository) GetReactions(ctx context.Context, channelID, ts string, full bool) ([]*model.SlackReaction, error) {
reactions, err := r.client.GetReaction(ctx, channelID, ts, full)
if err != nil {
return nil, err
}
ret := make([]*model.SlackReaction, 0, len(reactions))
for _, reaction := range reactions {
ret = append(ret, &model.SlackReaction{
Name: reaction.Name,
UserIDs: reaction.Users,
Count: reaction.Count,
})
}
return ret, nil
}
10 changes: 10 additions & 0 deletions app/pkg/slack/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Client interface {
PostEphemeral(ctx context.Context, channelID, userID, ts, message string) error
GetConversationReplies(ctx context.Context, channelID, ts string) ([]slack.Message, error)
GetUsersInfo(ctx context.Context, userID ...string) (*[]slack.User, error)
GetReaction(ctx context.Context, channelID, ts string, full bool) ([]slack.ItemReaction, error)

GetClient() *slack.Client
GetAppUserID() string
Expand Down Expand Up @@ -118,6 +119,15 @@ func (c *client) GetUsersInfo(ctx context.Context, userID ...string) (*[]slack.U
return users, nil
}

func (c *client) GetReaction(ctx context.Context, channelID, ts string, full bool) ([]slack.ItemReaction, error) {
return c.Client.GetReactionsContext(ctx, slack.ItemRef{
Channel: channelID,
Timestamp: ts,
}, slack.GetReactionsParameters{
Full: full,
})
}

func (c *client) GetClient() *slack.Client {
return c.Client
}
Expand Down