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

Sync emojis in comments #1

Closed
wants to merge 5 commits into from
Closed

Conversation

LokeshN
Copy link
Owner

@LokeshN LokeshN commented Jul 31, 2022

Copy link

@mickmister mickmister left a comment

Choose a reason for hiding this comment

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

Great work @LokeshN 🎉 I think this feature is going to provide lot of value

I have a few requests on how we can improve the code in regards to:

  • Handling emoji aliases and skin tones
  • De-duplicating some code
  • Supporting PRs and issues instead of just comments

Comment on lines +116 to +125
p.emojiMap = map[string]string{
"+1": "+1",
"-1": "-1",
"laughing": "laugh",
"confused": "confused",
"heart": "heart",
"tada": "hooray",
"rocket": "rocket",
"eyes": "eyes",
}

Choose a reason for hiding this comment

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

There are aliases such as thumbsup, and skin tone-specific versions like :-1_medium_light_skin_tone: that Mattermost users could have used for their reaction.

image

You can pre-populate a map for the skin tone-specific ones like this:

var baseGithubEmojiMap = map[string]string{
    "+1": "+1",
    "thumbsup": "+1",
}

var githubEmojiMap = map[string]string{}
for systemEmoji := range model.SystemEmojis { 
    for mmBase, ghBase := range baseGithubEmojiMap {
        if strings.HasPrefix(systemEmoji, mmBase) {
            githubEmojiMap[systemEmoji] = ghBase
        }
    }
}

Choose a reason for hiding this comment

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

Since emojiMap is a static piece of data, we can instead define it on the module level, rather than as a struct field. You can use this syntax on the module level:

var githubEmojiMap = map[string]string{}

Copy link

Choose a reason for hiding this comment

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

0/5 leaving it in p. and initializing in OnActivate (or NewPlugin) is just as idiomatic, for plugins.

if githubEmoji == "" {
return
}
post, error := p.client.Post.GetPost(reaction.PostId)

Choose a reason for hiding this comment

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

error is a an existing "type" in Go, so we shouldn't shadow it here

Suggested change
post, error := p.client.Post.GetPost(reaction.PostId)
post, err := p.client.Post.GetPost(reaction.PostId)

}
post, error := p.client.Post.GetPost(reaction.PostId)
if error != nil {
p.API.LogError("Invalid post id. Addition of reaction failed.")

Choose a reason for hiding this comment

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

Suggested change
p.API.LogError("Invalid post id. Addition of reaction failed.")
p.API.LogError("Error fetching post for reaction add", "error", err.Error())


repo := post.GetProp("gh_repo")
if repo == nil {
p.API.LogDebug("Required prop is not present. This post is not created by this plugin, hence ignoring")

Choose a reason for hiding this comment

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

Let's remove this log message. This will be the case for almost all reactions added on Mattermost, so it's not something we need to log here.

For further checks for legitimacy, we should also check to see if the post.UserId is the GitHub bot's user id

return
}

orgRepo := strings.Split(repo.(string), "/")

Choose a reason for hiding this comment

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

I suggest we make the code more compact by doing nil checking and type assertion in the same line. The ok syntax is the "safer" way of doing a type assertion. Otherwise if repo is not a string, the program will panic. That is highly unlikely here but should always be treated as possible.

Suggested change
orgRepo := strings.Split(repo.(string), "/")
repo, ok := post.GetProp("gh_repo").(string)
if !ok || repo == "" {
return
}
orgRepo := strings.Split(repo, "/")

Choose a reason for hiding this comment

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

Suggested change
orgRepo := strings.Split(repo.(string), "/")
orgRepo := strings.Split(repo.(string), "/")
if len(orgRepo) != 2 {
// handle error
}


reactions, _, error := ghClient.Reactions.ListIssueCommentReactions(context.Background(), orgRepo[0], orgRepo[1], int64(id), &github.ListOptions{})

if error == nil {

Choose a reason for hiding this comment

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

We should "return early" instead of having this indented block for the rest of the function

Suggested change
if error == nil {
if err != nil {
p.API.LogDebug(...)
return

}
post, error := p.client.Post.GetPost(reaction.PostId)
if error != nil {
p.API.LogError("Invalid post id. Removal of reaction failed.")

Choose a reason for hiding this comment

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

Please apply the change requests for ReactionHasBeenAdded to this function as well. Also, there is a decent amount of code duplication among these two functions. Can you please try extracting some code out to make the functions a bit cleaner?

It may be better to keep it as-is, since there are different "error" and "harmless skip" cases here, and it could be complicated to handle the different "log error" versus "just return" cases between the caller and callee.


if error == nil {
for _, reactionObj := range reactions {
if info.UserID == reaction.UserId && p.emojiMap[reaction.EmojiName] == reactionObj.GetContent() {

Choose a reason for hiding this comment

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

p.emojiMap[reaction.EmojiName] should ideally already be computed as a local variable to avoid verbosity on this long line

emoji := p.emojiMap[reaction.EmojiName]

// delete this reaction
_, error = ghClient.Reactions.DeleteIssueCommentReaction(context.Background(), orgRepo[0], orgRepo[1], int64(id), reactionObj.GetID())
if error != nil {
p.API.LogError("Error found while removing issue comment reaction")

Choose a reason for hiding this comment

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

Suggested change
p.API.LogError("Error found while removing issue comment reaction")
p.API.LogDebug("Error removing issue comment reaction", "error", err.Error())

Comment on lines +705 to +710
repoName := repo.GetFullName()
repoName = strings.ToLower(repoName)
commentID := event.GetComment().GetID()

post.AddProp("gh_repo", repoName)
post.AddProp("gh_object_id", commentID)

Choose a reason for hiding this comment

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

This should be applied to any webhook event associated with a PR or issue, not just the "issue comment" events. For example, if a new pull request is created, I should be able to react to the post in Mattermost and have my reaction on the PR itself in GitHub. Probably best to try to de-duplicate this code addition as well.

I'm thinking we will need to add an gh_object_type or gh_object_name post prop in order know what github API call to make, e.g. DeleteIssueCommentReaction vs DeleteIssueReaction

@mickmister
Copy link

@LokeshN I have one more request to add. Can you please write unit tests for the ReactionHasBeenAdded and ReactionHasBeenRemoved functions? Thank you!

Copy link

@levb levb left a comment

Choose a reason for hiding this comment

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

Mostly 👍 -ing @mickmister's comments

Comment on lines +116 to +125
p.emojiMap = map[string]string{
"+1": "+1",
"-1": "-1",
"laughing": "laugh",
"confused": "confused",
"heart": "heart",
"tada": "hooray",
"rocket": "rocket",
"eyes": "eyes",
}
Copy link

Choose a reason for hiding this comment

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

0/5 leaving it in p. and initializing in OnActivate (or NewPlugin) is just as idiomatic, for plugins.

@@ -702,6 +702,13 @@ func (p *Plugin) postIssueCommentEvent(event *github.IssueCommentEvent) {
Type: "custom_git_comment",
}

repoName := repo.GetFullName()
Copy link

Choose a reason for hiding this comment

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

0/5 collapse into repoName := strings.ToLower(repo.GetFullName()) for readability.

Comment on lines +287 to +292
info, appErr := p.getGitHubUserInfo(reaction.UserId)
if appErr != nil {
if appErr.ID != apiErrorIDNotConnected {
p.API.LogError("Error in getting user info", "error", appErr.Message)
}
}
Copy link

Choose a reason for hiding this comment

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

👍 important

if appErr != nil {
if appErr.ID != apiErrorIDNotConnected {
p.API.LogError("Error in getting user info", "error", appErr.Message)
}
Copy link

Choose a reason for hiding this comment

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

return

@LokeshN
Copy link
Owner Author

LokeshN commented Aug 8, 2022

Thanks for the comments, I will take this up separately in a new PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature: Synchronize emoji reactions on posts generated by the plugin
3 participants