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

[GH-618]: Added slack attachment for webhook posts, in order to comment, edit, and close issues. #636

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
cbb804e
[MI-2502]:Added custom type for webhook post in order to comment, edi…
Nityanand13 Jan 31, 2023
4378034
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Nityanand13 Jan 31, 2023
20ab25d
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Nityanand13 Feb 8, 2023
70ae885
[MI-2736]: Done the review fixes of a github PR #636 (#20)
Nityanand13 Feb 9, 2023
3cecd5d
[MI-2736]: Done the review fixes of a github PR #636 (#21)
Nityanand13 Feb 16, 2023
779f10f
[MI-2814] Done the review fixes of github PR #636 (#22)
Nityanand13 Mar 13, 2023
67c370b
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Nityanand13 Mar 13, 2023
2de4435
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
ayusht2810 Apr 9, 2024
0f40aba
[MM-618] Update validations, modal title, styling, post messages, con…
ayusht2810 Apr 10, 2024
f4f5057
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
ayusht2810 Apr 24, 2024
9f7af14
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
ayusht2810 May 1, 2024
d0e7abd
[MM-618] Remove constants and serializers package
ayusht2810 May 1, 2024
1a279e8
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
ayusht2810 Jun 21, 2024
39fe3cf
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
raghavaggarwal2308 Jul 2, 2024
c5a8d29
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
raghavaggarwal2308 Jul 5, 2024
136221f
[MM-556] Review fixes
raghavaggarwal2308 Jul 5, 2024
482fcf9
[MM-617]: converted the custom post to slack attachment for issue cre…
Kshitij-Katiyar Aug 6, 2024
79c266e
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Kshitij-Katiyar Aug 6, 2024
28300d5
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Kshitij-Katiyar Aug 30, 2024
b413828
[MM-618]: Fixed the lint
Kshitij-Katiyar Aug 30, 2024
64655a0
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Kshitij-Katiyar Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
602 changes: 443 additions & 159 deletions server/plugin/api.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion server/plugin/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func TestPlugin_ServeHTTP(t *testing.T) {
p.SetAPI(&plugintest.API{})

req := test.httpTest.CreateHTTPRequest(test.request)
req.Header.Add("Mattermost-User-ID", test.userID)
req.Header.Add(headerMattermostUserID, test.userID)
rr := httptest.NewRecorder()
p.ServeHTTP(&plugin.Context{}, rr, req)
test.httpTest.CompareHTTPResponse(rr, test.expectedResponse)
Expand Down
11 changes: 11 additions & 0 deletions server/plugin/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package plugin

type APIErrorResponse struct {
ID string `json:"id"`
Message string `json:"message"`
StatusCode int `json:"status_code"`
}

func (e *APIErrorResponse) Error() string {
return e.Message
}
2 changes: 1 addition & 1 deletion server/plugin/mm_34646_token_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (p *Plugin) forceResetUserTokenMM34646(ctx context.Context, config *Configu
info.MM34646ResetTokenDone = true
err = p.storeGitHubUserInfo(info)
if err != nil {
return "", errors.Wrap(err, "failed to store updated GitHubUserInfo")
return "", errors.Wrap(err, "failed to store updated serializer.GitHubUserInfo")
}
p.client.Log.Debug("Updated user access token for MM-34646", "user_id", info.UserID)

Expand Down
58 changes: 58 additions & 0 deletions server/plugin/oauth.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,72 @@
package plugin

import (
"context"
"sync"

"github.com/mattermost/mattermost/server/public/pluginapi/experimental/bot/logger"
"golang.org/x/oauth2"
)

type Context struct {
Ctx context.Context
UserID string
Log logger.Logger
}

type GitHubUserRequest struct {
UserID string `json:"user_id"`
}

type GitHubUserResponse struct {
Username string `json:"username"`
}

type ConnectedResponse struct {
Connected bool `json:"connected"`
GitHubUsername string `json:"github_username"`
GitHubClientID string `json:"github_client_id"`
EnterpriseBaseURL string `json:"enterprise_base_url,omitempty"`
Organizations []string `json:"organizations"`
UserSettings *UserSettings `json:"user_settings"`
ClientConfiguration map[string]interface{} `json:"configuration"`
}

type UserSettings struct {
SidebarButtons string `json:"sidebar_buttons"`
DailyReminder bool `json:"daily_reminder"`
DailyReminderOnChange bool `json:"daily_reminder_on_change"`
Notifications bool `json:"notifications"`
}

type GitHubUserInfo struct {
UserID string
Token *oauth2.Token
GitHubUsername string
LastToDoPostAt int64
Settings *UserSettings
AllowedPrivateRepos bool

// MM34646ResetTokenDone is set for a user whose token has been reset for MM-34646.
MM34646ResetTokenDone bool
}

type OAuthCompleteEvent struct {
UserID string
Err error
}

type UserContext struct {
Context
GHInfo *GitHubUserInfo
}

type OAuthState struct {
UserID string `json:"user_id"`
Token string `json:"token"`
PrivateAllowed bool `json:"private_allowed"`
}

type OAuthBroker struct {
sendOAuthCompleteEvent func(event OAuthCompleteEvent)

Expand Down
101 changes: 62 additions & 39 deletions server/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package plugin

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
Expand All @@ -11,6 +10,7 @@ import (
"regexp"
"strings"
"sync"
"time"

"github.com/google/go-github/v54/github"
"github.com/gorilla/mux"
Expand All @@ -28,10 +28,14 @@ import (
)

const (
githubTokenKey = "_githubtoken"
githubOauthKey = "githuboauthkey_"
githubUsernameKey = "_githubusername"
githubPrivateRepoKey = "_githubprivate"
githubTokenKey = "_githubtoken"
githubOauthKey = "githuboauthkey_"
githubUsernameKey = "_githubusername"
githubPrivateRepoKey = "_githubprivate"
githubObjectTypeIssue = "issue"
githubObjectTypeIssueComment = "issue_comment"
githubObjectTypePRReviewComment = "pr_review_comment"
githubObjectTypeDiscussionComment = "discussion_comment"

mm34646MutexKey = "mm34646_token_reset_mutex"
mm34646DoneKey = "mm34646_token_reset_done"
Expand All @@ -56,6 +60,54 @@ const (
dailySummary = "_dailySummary"

chimeraGitHubAppIdentifier = "plugin-github"

apiErrorIDNotConnected = "not_connected"

// TokenTTL is the OAuth token expiry duration in seconds
tokenTTL = 600

requestTimeout = 30 * time.Second
oauthCompleteTimeout = 2 * time.Minute
headerMattermostUserID = "Mattermost-User-ID"
ownerQueryParam = "owner"
repoQueryParam = "repo"
numberQueryParam = "number"
postIDQueryParam = "postId"

issueStatus = "status"
assigneesForProps = "assignees"
labelsForProps = "labels"
descriptionForProps = "description"
titleForProps = "title"
attachmentsForProps = "attachments"
issueNumberForProps = "issue_number"
issueURLForProps = "issue_url"
repoOwnerForProps = "repo_owner"
repoNameForProps = "repo_name"

statusClose = "Close"
statusReopen = "Reopen"

issueCompleted = "completed"
issueNotPlanned = "not_planned"
issueClose = "closed"
issueOpen = "open"

// Actions of webhook events
actionOpened = "opened"
actionClosed = "closed"
actionReopened = "reopened"
actionSubmitted = "submitted"
actionLabeled = "labeled"
actionAssigned = "assigned"
actionCreated = "created"
actionDeleted = "deleted"
actionEdited = "edited"
actionMarkedReadyForReview = "ready_for_review"

postPropGithubRepo = "gh_repo"
postPropGithubObjectID = "gh_object_id"
postPropGithubObjectType = "gh_object_type"
)

var (
Expand Down Expand Up @@ -586,25 +638,6 @@ func (p *Plugin) getOAuthConfigForChimeraApp(scopes []string) *oauth2.Config {
}
}

type GitHubUserInfo struct {
UserID string
Token *oauth2.Token
GitHubUsername string
LastToDoPostAt int64
Settings *UserSettings
AllowedPrivateRepos bool

// MM34646ResetTokenDone is set for a user whose token has been reset for MM-34646.
MM34646ResetTokenDone bool
}

type UserSettings struct {
SidebarButtons string `json:"sidebar_buttons"`
DailyReminder bool `json:"daily_reminder"`
DailyReminderOnChange bool `json:"daily_reminder_on_change"`
Notifications bool `json:"notifications"`
}

func (p *Plugin) storeGitHubUserInfo(info *GitHubUserInfo) error {
config := p.getConfiguration()

Expand Down Expand Up @@ -1037,7 +1070,7 @@ func (p *Plugin) sendRefreshEvent(userID string) {
return
}

contentMap, err := sidebarContent.toMap()
contentMap, err := sidebarContent.ToMap()
if err != nil {
p.client.Log.Warn("Failed to convert sidebar content to map", "error", err.Error())
return
Expand All @@ -1050,20 +1083,6 @@ func (p *Plugin) sendRefreshEvent(userID string) {
)
}

func (s *SidebarContent) toMap() (map[string]interface{}, error) {
var m map[string]interface{}
bytes, err := json.Marshal(&s)
if err != nil {
return nil, err
}

if err = json.Unmarshal(bytes, &m); err != nil {
return nil, err
}

return m, nil
}

// getUsername returns the GitHub username for a given Mattermost user,
// if the user is connected to GitHub via this plugin.
// Otherwise it return the Mattermost username. It will be escaped via backticks.
Expand All @@ -1084,3 +1103,7 @@ func (p *Plugin) getUsername(mmUserID string) (string, error) {

return "@" + info.GitHubUsername, nil
}

func (p *Plugin) GetPluginAPIPath() string {
return fmt.Sprintf("%s/plugins/%s/api/v1", *p.client.Configuration.GetConfig().ServiceSettings.SiteURL, Manifest.Id)
}
33 changes: 33 additions & 0 deletions server/plugin/sidebar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package plugin

import (
"encoding/json"

"github.com/google/go-github/v54/github"
)

type FilteredNotification struct {
github.Notification
HTMLURL string `json:"html_url"`
}

type SidebarContent struct {
PRs []*github.Issue `json:"prs"`
Reviews []*github.Issue `json:"reviews"`
Assignments []*github.Issue `json:"assignments"`
Unreads []*FilteredNotification `json:"unreads"`
}

func (s *SidebarContent) ToMap() (map[string]interface{}, error) {
var m map[string]interface{}
bytes, err := json.Marshal(&s)
if err != nil {
return nil, err
}

if err = json.Unmarshal(bytes, &m); err != nil {
return nil, err
}

return m, nil
}
16 changes: 2 additions & 14 deletions server/plugin/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func init() {

// The repo template links to the corresponding repository.
template.Must(masterTemplate.New("repo").Parse(
`[\[{{.GetFullName}}\]]({{.GetHTMLURL}})`,
`[{{.GetFullName}}]({{.GetHTMLURL}})`,
))

// The eventRepoPullRequest links to the corresponding pull request, anchored at the repo.
Expand Down Expand Up @@ -256,19 +256,7 @@ Assignees: {{range $i, $el := .Assignees -}} {{- if $i}}, {{end}}{{template "use
{{.GetPullRequest.GetBody | trimBody | quote | replaceAllGitHubUsernames}}`))

template.Must(masterTemplate.New("newIssue").Funcs(funcMap).Parse(`
{{ if eq .Config.Style "collapsed" -}}
{{template "repo" .Event.GetRepo}} New issue {{template "issue" .Event.GetIssue}} opened by {{template "user" .Event.GetSender}}.
{{- else -}}
#### {{.Event.GetIssue.GetTitle}}
##### {{template "eventRepoIssue" .Event}}
#new-issue by {{template "user" .Event.GetSender}}
{{- if ne .Config.Style "skip-body" -}}
{{- template "labels" dict "Labels" .Event.GetIssue.Labels "RepositoryURL" .Event.GetRepo.GetHTMLURL }}
{{- template "assignee" .Event.GetIssue }}

{{.Event.GetIssue.GetBody | removeComments | replaceAllGitHubUsernames}}
{{- end -}}
{{- end }}
{{template "user" .Event.GetSender}} created a new issue in {{template "repo" .Event.GetRepo}}
`))

template.Must(masterTemplate.New("closedIssue").Funcs(funcMap).Parse(`
Expand Down
Loading
Loading