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-613]:Fixed issue "API rate limit exceeded for user ID". #626

Merged
merged 20 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1abdab2
[MI-2481]:Fixed issue #613 on github (#12)
Kshitij-Katiyar Dec 30, 2022
7ab3af2
[MI-2547]:Fixed review fixes for Github issue 613 and PR 626 (#14)
Kshitij-Katiyar Jan 6, 2023
862efaa
[MI-2582]:Fixed review comments for github issue #613 (#15)
Kshitij-Katiyar Jan 9, 2023
daeb982
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Kshitij-Katiyar Jan 10, 2023
c792d99
Merge branch 'MM-613' of github.com:Brightscout/mattermost-plugin-git…
Kshitij-Katiyar Jan 10, 2023
6c80325
[MM-613]:Fixed review comments
Kshitij-Katiyar Feb 16, 2023
9388908
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Kshitij-Katiyar Feb 22, 2023
3f9db5f
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Kshitij-Katiyar Feb 28, 2023
9fa4533
[GH-613]:Fixed review comments
Kshitij-Katiyar May 8, 2023
c30c813
[MI-3072]:Converted the LHS APIs into GraphQL (#32)
Kshitij-Katiyar May 26, 2023
1210de0
[MM-613]:Changed namings
Kshitij-Katiyar May 26, 2023
609c781
[MM-613]:Fixed review comments
Kshitij-Katiyar May 31, 2023
5e3ca85
[MM-613]:Fixed review comments
Kshitij-Katiyar Jun 1, 2023
935d917
Merge branch 'master' of github.com:mattermost/mattermost-plugin-gith…
Kshitij-Katiyar Jun 30, 2023
7815f49
[MM-613]:Fixed panic error
Kshitij-Katiyar Jun 30, 2023
2571bb1
[MM-613]:Fixed review comments
Kshitij-Katiyar Jul 5, 2023
e8695ee
[MM-613]:Fixed lint errors
Kshitij-Katiyar Jul 5, 2023
3eec349
[MM-613]:Fixed review comment
Kshitij-Katiyar Jul 5, 2023
55cae89
[MM-613]:Fixed review comments
Kshitij-Katiyar Jul 10, 2023
772c8c3
[MM-613]:Fixed review comments
Kshitij-Katiyar Jul 11, 2023
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
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ require (
github.com/rudderlabs/analytics-go v3.3.2+incompatible // indirect
github.com/segmentio/backo-go v1.0.1 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/shurcooL/githubv4 v0.0.0-20230424031643-6cea62ecd5a9
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/stretchr/objx v0.4.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,14 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/githubv4 v0.0.0-20230424031643-6cea62ecd5a9 h1:nCBaIs5/R0HFP5+aPW/SzFUF8z0oKuCXmuDmHWaxzjY=
github.com/shurcooL/githubv4 v0.0.0-20230424031643-6cea62ecd5a9/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc=
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
Expand Down
102 changes: 46 additions & 56 deletions server/plugin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ type PRDetails struct {
Reviews []*github.PullRequestReview `json:"reviews"`
}

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"`
}

type Context struct {
Ctx context.Context
UserID string
Expand Down Expand Up @@ -134,22 +146,19 @@ func (p *Plugin) initializeAPI() {

apiRouter.HandleFunc("/user", p.checkAuth(p.attachContext(p.getGitHubUser), ResponseTypeJSON)).Methods(http.MethodPost)
apiRouter.HandleFunc("/todo", p.checkAuth(p.attachUserContext(p.postToDo), ResponseTypeJSON)).Methods(http.MethodPost)
apiRouter.HandleFunc("/reviews", p.checkAuth(p.attachUserContext(p.getReviews), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/yourprs", p.checkAuth(p.attachUserContext(p.getYourPrs), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/prsdetails", p.checkAuth(p.attachUserContext(p.getPrsDetails), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/searchissues", p.checkAuth(p.attachUserContext(p.searchIssues), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/yourassignments", p.checkAuth(p.attachUserContext(p.getYourAssignments), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/createissue", p.checkAuth(p.attachUserContext(p.createIssue), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/createissuecomment", p.checkAuth(p.attachUserContext(p.createIssueComment), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/mentions", p.checkAuth(p.attachUserContext(p.getMentions), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/unreads", p.checkAuth(p.attachUserContext(p.getUnreads), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/labels", p.checkAuth(p.attachUserContext(p.getLabels), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/milestones", p.checkAuth(p.attachUserContext(p.getMilestones), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/assignees", p.checkAuth(p.attachUserContext(p.getAssignees), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/repositories", p.checkAuth(p.attachUserContext(p.getRepositories), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/settings", p.checkAuth(p.attachUserContext(p.updateSettings), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/issue", p.checkAuth(p.attachUserContext(p.getIssueByNumber), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/pr", p.checkAuth(p.attachUserContext(p.getPrByNumber), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/lhs-content", p.checkAuth(p.attachUserContext(p.getSidebarContent), ResponseTypePlain)).Methods(http.MethodGet)

apiRouter.HandleFunc("/config", checkPluginRequest(p.getConfig)).Methods(http.MethodGet)
apiRouter.HandleFunc("/token", checkPluginRequest(p.getToken)).Methods(http.MethodGet)
Expand Down Expand Up @@ -651,22 +660,16 @@ func (p *Plugin) getMentions(c *UserContext, w http.ResponseWriter, r *http.Requ
p.writeJSON(w, result.Issues)
}

func (p *Plugin) getUnreads(c *UserContext, w http.ResponseWriter, r *http.Request) {
func (p *Plugin) getUnreadsData(c *UserContext) []*FilteredNotification {
githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo)

notifications, _, err := githubClient.Activity.ListNotifications(c.Ctx, &github.NotificationListOptions{})
if err != nil {
c.Log.WithError(err).Warnf("Failed to list notifications")
return
}

type filteredNotification struct {
github.Notification

HTMLUrl string `json:"html_url"`
return nil
}

filteredNotifications := []*filteredNotification{}
filteredNotifications := []*FilteredNotification{}
for _, n := range notifications {
if n.GetReason() == notificationReasonSubscribed {
continue
Expand All @@ -684,45 +687,13 @@ func (p *Plugin) getUnreads(c *UserContext, w http.ResponseWriter, r *http.Reque
subjectURL = n.GetSubject().GetLatestCommentURL()
}

filteredNotifications = append(filteredNotifications, &filteredNotification{
filteredNotifications = append(filteredNotifications, &FilteredNotification{
Notification: *n,
HTMLUrl: fixGithubNotificationSubjectURL(subjectURL, issueNum),
HTMLURL: fixGithubNotificationSubjectURL(subjectURL, issueNum),
})
}

p.writeJSON(w, filteredNotifications)
}

func (p *Plugin) getReviews(c *UserContext, w http.ResponseWriter, r *http.Request) {
config := p.getConfiguration()

githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo)
username := c.GHInfo.GitHubUsername

query := getReviewSearchQuery(username, config.GitHubOrg)
result, _, err := githubClient.Search.Issues(c.Ctx, query, &github.SearchOptions{})
if err != nil {
c.Log.WithError(err).With(logger.LogContext{"query": query}).Warnf("Failed to search for review")
return
}

p.writeJSON(w, result.Issues)
}

func (p *Plugin) getYourPrs(c *UserContext, w http.ResponseWriter, r *http.Request) {
config := p.getConfiguration()

githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo)
username := c.GHInfo.GitHubUsername

query := getYourPrsSearchQuery(username, config.GitHubOrg)
result, _, err := githubClient.Search.Issues(c.Ctx, query, &github.SearchOptions{})
if err != nil {
c.Log.WithError(err).With(logger.LogContext{"query": query}).Warnf("Failed to search for PRs")
return
}

p.writeJSON(w, result.Issues)
return filteredNotifications
}

func (p *Plugin) getPrsDetails(c *UserContext, w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -967,20 +938,39 @@ func (p *Plugin) createIssueComment(c *UserContext, w http.ResponseWriter, r *ht
p.writeJSON(w, result)
}

func (p *Plugin) getYourAssignments(c *UserContext, w http.ResponseWriter, r *http.Request) {
config := p.getConfiguration()
func (p *Plugin) getLHSData(c *UserContext) (reviewResp []*github.Issue, assignmentResp []*github.Issue, openPRResp []*github.Issue, err error) {
graphQLClient := p.graphQLConnect(c.GHInfo)

githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo)
reviewResp, assignmentResp, openPRResp, err = graphQLClient.GetLHSData(c.Context.Ctx)
if err != nil {
return []*github.Issue{}, []*github.Issue{}, []*github.Issue{}, err
}

username := c.GHInfo.GitHubUsername
query := getYourAssigneeSearchQuery(username, config.GitHubOrg)
result, _, err := githubClient.Search.Issues(c.Ctx, query, &github.SearchOptions{})
return reviewResp, assignmentResp, openPRResp, nil
}

func (p *Plugin) getSidebarData(c *UserContext) (*SidebarContent, error) {
reviweResp, assignmentResp, openPRResp, err := p.getLHSData(c)
Kshitij-Katiyar marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
c.Log.WithError(err).With(logger.LogContext{"query": query}).Warnf("Failed to search for assignments")
return nil, err
}

return &SidebarContent{
PRs: openPRResp,
Assignments: assignmentResp,
Reviews: reviweResp,
Unreads: p.getUnreadsData(c),
}, nil
}

func (p *Plugin) getSidebarContent(c *UserContext, w http.ResponseWriter, r *http.Request) {
sidebarContent, err := p.getSidebarData(c)
if err != nil {
c.Log.WithError(err).Warnf("Failed to search for the sidebar data")
return
}

p.writeJSON(w, result.Issues)
p.writeJSON(w, sidebarContent)
}

func (p *Plugin) postToDo(c *UserContext, w http.ResponseWriter, r *http.Request) {
Expand Down
2 changes: 1 addition & 1 deletion server/plugin/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func TestPlugin_ServeHTTP(t *testing.T) {
httpTest: httpTestString,
request: testutils.Request{
Method: http.MethodGet,
URL: "/api/v1/reviews",
URL: "/api/v1/lhs-content",
Body: nil,
},
expectedResponse: testutils.ExpectedResponse{
Expand Down
62 changes: 62 additions & 0 deletions server/plugin/graphql/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package graphql

import (
"context"
"net/url"
"path"

pluginapi "github.com/mattermost/mattermost-plugin-api"
"github.com/pkg/errors"
"github.com/shurcooL/githubv4"
"golang.org/x/oauth2"
)

// Client encapsulates the third party package that communicates with Github GraphQL API
type Client struct {
client *githubv4.Client
org string
username string
logger pluginapi.LogService
}

// NewClient creates and returns Client. The third party package that queries GraphQL is initialized here.
func NewClient(logger pluginapi.LogService, token oauth2.Token, username, orgName, enterpriseBaseURL string) *Client {
ts := oauth2.StaticTokenSource(&token)
httpClient := oauth2.NewClient(context.Background(), ts)
var client Client

if enterpriseBaseURL == "" {
client = Client{
username: username,
client: githubv4.NewClient(httpClient),
logger: logger,
org: orgName,
}
} else {
baseURL, err := url.Parse(enterpriseBaseURL)
if err != nil {
logger.Debug("Not able to parse the URL", "Error", err.Error())
return nil
}

baseURL.Path = path.Join(baseURL.Path, "api", "graphql")

client = Client{
client: githubv4.NewEnterpriseClient(baseURL.String(), httpClient),
username: username,
org: orgName,
logger: logger,
}
}

return &client
}

// executeQuery takes a query struct and sends it to Github GraphQL API via helper package.
func (c *Client) executeQuery(ctx context.Context, qry interface{}, params map[string]interface{}) error {
if err := c.client.Query(ctx, qry, params); err != nil {
return errors.Wrap(err, "error in executing query")
}

return nil
}
91 changes: 91 additions & 0 deletions server/plugin/graphql/lhs_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package graphql

import (
"github.com/shurcooL/githubv4"
)

type (
repositoryQuery struct {
Name githubv4.String
NameWithOwner githubv4.String
URL githubv4.URI
}

authorQuery struct {
Login githubv4.String
}

prSearchNodes struct {
PullRequest struct {
Body githubv4.String
Number githubv4.Int
AuthorAssociation githubv4.String
CreatedAt githubv4.DateTime
UpdatedAt githubv4.DateTime
Repository repositoryQuery
State githubv4.String
Title githubv4.String
Author authorQuery
URL githubv4.URI
} `graphql:"... on PullRequest"`
}
)

type (
assignmentSearchNodes struct {
Issue struct {
Body githubv4.String
Number githubv4.Int
AuthorAssociation githubv4.String
CreatedAt githubv4.DateTime
UpdatedAt githubv4.DateTime
Repository repositoryQuery
State githubv4.String
Title githubv4.String
Author authorQuery
URL githubv4.URI
} `graphql:"... on Issue"`

PullRequest struct {
Body githubv4.String
Number githubv4.Int
AuthorAssociation githubv4.String
CreatedAt githubv4.DateTime
UpdatedAt githubv4.DateTime
Repository repositoryQuery
State githubv4.String
Title githubv4.String
Author authorQuery
URL githubv4.URI
} `graphql:"... on PullRequest"`
}
)

var mainQuery struct {
PullRequest struct {
Kshitij-Katiyar marked this conversation as resolved.
Show resolved Hide resolved
IssueCount int
Nodes []prSearchNodes
PageInfo struct {
EndCursor githubv4.String
HasNextPage bool
}
} `graphql:"pullRequest: search(first:100, after:$reviewCursor, query: $prReviewQueryArg, type: ISSUE)"`

Assignee struct {
Kshitij-Katiyar marked this conversation as resolved.
Show resolved Hide resolved
IssueCount int
Nodes []assignmentSearchNodes
PageInfo struct {
EndCursor githubv4.String
HasNextPage bool
}
} `graphql:"assignee: search(first:100, after:$assignmentsCursor, query: $assigneeQueryArg, type: ISSUE)"`

OpenPullRequest struct {
Kshitij-Katiyar marked this conversation as resolved.
Show resolved Hide resolved
IssueCount int
Nodes []prSearchNodes
PageInfo struct {
EndCursor githubv4.String
HasNextPage bool
}
} `graphql:"graphql: search(first:100, after:$openPrsCursor, query: $prOpenQueryArg, type: ISSUE)"`
}
Loading