From e2da6e17f3b5dd9a037e9ca5778b1d9b370b88b0 Mon Sep 17 00:00:00 2001 From: Edvard Makhlin Date: Mon, 29 Apr 2024 14:35:33 +0200 Subject: [PATCH 1/6] hide-bb-comments: This should allow to delete previous comments in a PR --- server/events/vcs/bitbucketcloud/client.go | 77 +++++++++++++++++++++- server/events/vcs/bitbucketcloud/models.go | 28 ++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/server/events/vcs/bitbucketcloud/client.go b/server/events/vcs/bitbucketcloud/client.go index a0dcfd7161..df337812ef 100644 --- a/server/events/vcs/bitbucketcloud/client.go +++ b/server/events/vcs/bitbucketcloud/client.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "strings" "unicode/utf8" validator "github.com/go-playground/validator/v10" @@ -107,10 +108,84 @@ func (b *Client) ReactToComment(_ logging.SimpleLogging, _ models.Repo, _ int, _ return nil } -func (b *Client) HidePrevCommandComments(_ logging.SimpleLogging, _ models.Repo, _ int, _ string, _ string) error { +func (b *Client) HidePrevCommandComments(logger logging.SimpleLogging, repo models.Repo, pullNum int, command string, _ string) error { + // there is no way to hide comment, so delete them instead + me, err := b.GetMyUUID() + if err != nil { + return errors.Wrapf(err, "Cannot get my uuid! Please check required scope of the auth token!") + } + logger.Debug("My bitbucket user UUID is: %s", me) + + comments, err := b.GetPullRequestComments(repo, pullNum) + if err != nil { + return err + } + + for _, c := range comments { + logger.Debug("Comment is %v", c.Content.Raw) + if strings.EqualFold(*c.User.UUID, me) { + // do the same crude filtering as github client does + body := strings.Split(c.Content.Raw, "\n") + logger.Debug("Body is %s", body) + if len(body) == 0 { + continue + } + firstLine := strings.ToLower(body[0]) + if strings.Contains(firstLine, strings.ToLower(command)) { + // we found our old comment that references that command + logger.Debug("Deleting comment with id %s", *c.ID) + b.DeletePullRequestComment(repo, pullNum, *c.ID) + } + } + } + return nil +} + +func (b *Client) DeletePullRequestComment(repo models.Repo, pullNum int, commentId int) error { + path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d/comments/%d", b.BaseURL, repo.FullName, pullNum, commentId) + _, err := b.makeRequest("DELETE", path, nil) + if err != nil { + return err + } return nil } +func (b *Client) GetPullRequestComments(repo models.Repo, pullNum int) (comments []PullRequestComment, err error) { + path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d/comments", b.BaseURL, repo.FullName, pullNum) + res, err := b.makeRequest("GET", path, nil) + if err != nil { + return comments, err + } + + var pulls PullRequestComments + if err := json.Unmarshal(res, &pulls); err != nil { + return comments, errors.Wrapf(err, "Could not parse response %q", string(res)) + } + return pulls.Values, nil +} + +func (b *Client) GetMyUUID() (uuid string, err error) { + path := fmt.Sprintf("%s/2.0/user", b.BaseURL) + resp, err := b.makeRequest("GET", path, nil) + + if err != nil { + return uuid, err + } + + var user User + if err := json.Unmarshal(resp, &user); err != nil { + return uuid, errors.Wrapf(err, "Could not parse response %q", string(resp)) + } + + if err := validator.New().Struct(user); err != nil { + return uuid, errors.Wrapf(err, "API response %q was missing a field", string(resp)) + } + + uuid = *user.UUID + return uuid, nil + +} + // PullIsApproved returns true if the merge request was approved. func (b *Client) PullIsApproved(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest) (approvalStatus models.ApprovalStatus, err error) { path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d", b.BaseURL, repo.FullName, pull.Num) diff --git a/server/events/vcs/bitbucketcloud/models.go b/server/events/vcs/bitbucketcloud/models.go index 1da27ebbe3..d33fc14da6 100644 --- a/server/events/vcs/bitbucketcloud/models.go +++ b/server/events/vcs/bitbucketcloud/models.go @@ -45,6 +45,34 @@ type Repository struct { FullName *string `json:"full_name,omitempty" validate:"required"` Links Links `json:"links,omitempty" validate:"required"` } + +type User struct { + Type *string `json:"type,omitempty" validate:"required"` + CreateOn *string `json:"created_on" validate:"required"` + DisplayName *string `json:"display_name" validate:"required"` + Username *string `json:"username" validate:"required"` + UUID *string `json:"uuid" validate:"required"` +} + +type UserInComment struct { + Type *string `json:"type,omitempty" validate:"required"` + Nickname *string `json:"nickname" validate:"required"` + DisplayName *string `json:"display_name" validate:"required"` + UUID *string `json:"uuid" validate:"required"` +} + +type PullRequestComment struct { + ID *int `json:"id,omitempty" validate:"required"` + User *UserInComment `json:"user" validate:"required"` + Content *struct { + Raw string `json:"raw"` + } `json:"content" validate:"required"` +} + +type PullRequestComments struct { + Values []PullRequestComment `json:"values,omitempty"` +} + type PullRequest struct { ID *int `json:"id,omitempty" validate:"required"` Source *BranchMeta `json:"source,omitempty" validate:"required"` From 38cf3318661a1350361f405129ded621c504847a Mon Sep 17 00:00:00 2001 From: Edvard Makhlin Date: Tue, 7 May 2024 14:49:49 +0200 Subject: [PATCH 2/6] add tests --- .../events/vcs/bitbucketcloud/client_test.go | 150 ++++++++++ .../vcs/bitbucketcloud/testdata/comments.json | 272 ++++++++++++++++++ .../vcs/bitbucketcloud/testdata/user.json | 33 +++ 3 files changed, 455 insertions(+) create mode 100644 server/events/vcs/bitbucketcloud/testdata/comments.json create mode 100644 server/events/vcs/bitbucketcloud/testdata/user.json diff --git a/server/events/vcs/bitbucketcloud/client_test.go b/server/events/vcs/bitbucketcloud/client_test.go index e7def22b66..833e022d11 100644 --- a/server/events/vcs/bitbucketcloud/client_test.go +++ b/server/events/vcs/bitbucketcloud/client_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "strings" "testing" "github.com/runatlantis/atlantis/server/events/models" @@ -365,3 +366,152 @@ func TestClient_MarkdownPullLink(t *testing.T) { exp := "#1" Equals(t, exp, s) } + +func TestClient_GetMyUUID(t *testing.T) { + json, err := os.ReadFile(filepath.Join("testdata", "user.json")) + Ok(t, err) + + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.RequestURI { + case "/2.0/user": + w.Write([]byte(json)) // nolint: errcheck + return + default: + t.Errorf("got unexpected request at %q", r.RequestURI) + http.Error(w, "not found", http.StatusNotFound) + return + } + })) + defer testServer.Close() + + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client.BaseURL = testServer.URL + v, _ := client.GetMyUUID() + Equals(t, v, "{00000000-0000-0000-0000-000000000001}") +} + +func TestClient_GetComment(t *testing.T) { + json, err := os.ReadFile(filepath.Join("testdata", "comments.json")) + Ok(t, err) + + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.RequestURI { + case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments": + w.Write([]byte(json)) // nolint: errcheck + return + default: + t.Errorf("got unexpected request at %q", r.RequestURI) + http.Error(w, "not found", http.StatusNotFound) + return + } + })) + defer testServer.Close() + + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client.BaseURL = testServer.URL + v, _ := client.GetPullRequestComments( + models.Repo{ + FullName: "myorg/myrepo", + Owner: "owner", + Name: "myrepo", + CloneURL: "", + SanitizedCloneURL: "", + VCSHost: models.VCSHost{ + Type: models.BitbucketCloud, + Hostname: "bitbucket.org", + }, + }, 5) + + Equals(t, len(v), 5) + exp := "Plan" + Assert(t, strings.Contains(v[1].Content.Raw, exp), "Comment should contain word \"%s\", has \"%s\"", exp, v[1].Content.Raw) +} + +func TestClient_DeleteComment(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.RequestURI { + case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/1": + w.Write([]byte("")) // nolint: errcheck + return + default: + t.Errorf("got unexpected request at %q", r.RequestURI) + http.Error(w, "not found", http.StatusNotFound) + return + } + })) + defer testServer.Close() + + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client.BaseURL = testServer.URL + err := client.DeletePullRequestComment( + models.Repo{ + FullName: "myorg/myrepo", + Owner: "owner", + Name: "myrepo", + CloneURL: "", + SanitizedCloneURL: "", + VCSHost: models.VCSHost{ + Type: models.BitbucketCloud, + Hostname: "bitbucket.org", + }, + }, 5, 1) + Ok(t, err) +} + +func TestClient_HidePRComments(t *testing.T) { + logger := logging.NewNoopLogger(t) + comments, err := os.ReadFile(filepath.Join("testdata", "comments.json")) + Ok(t, err) + json, err := os.ReadFile(filepath.Join("testdata", "user.json")) + Ok(t, err) + + called := 0 + + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.RequestURI { + // we have two comments in the test file + // The code is going to delete them all and then create a new one + case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/498931882": + w.Write([]byte("")) + called += 1 + return + // This is the second one + case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/498931784": + w.Write([]byte("")) // nolint: errcheck + called += 1 + return + case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/49893111": + Assert(t, r.Method != "DELETE", "Shouldn't delete this one") + return + case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments": + w.Write([]byte(comments)) // nolint: errcheck + return + case "/2.0/user": + w.Write([]byte(json)) // nolint: errcheck + return + default: + t.Errorf("got unexpected request at %q", r.RequestURI) + http.Error(w, "not found", http.StatusNotFound) + return + } + })) + defer testServer.Close() + + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client.BaseURL = testServer.URL + err = client.HidePrevCommandComments(logger, + models.Repo{ + FullName: "myorg/myrepo", + Owner: "owner", + Name: "myrepo", + CloneURL: "", + SanitizedCloneURL: "", + VCSHost: models.VCSHost{ + Type: models.BitbucketCloud, + Hostname: "bitbucket.org", + }, + }, 5, "plan", "") + Ok(t, err) + Equals(t, 2, called) +} + diff --git a/server/events/vcs/bitbucketcloud/testdata/comments.json b/server/events/vcs/bitbucketcloud/testdata/comments.json new file mode 100644 index 0000000000..a697b1ab00 --- /dev/null +++ b/server/events/vcs/bitbucketcloud/testdata/comments.json @@ -0,0 +1,272 @@ +{ + "values": [ + { + "id": 498931784, + "created_on": "2024-05-07T12:21:45.858898+00:00", + "updated_on": "2024-05-07T12:21:45.859011+00:00", + "content": { + "type": "rendered", + "raw": "atlantis plan", + "markup": "markdown", + "html": "

atlantis plan

" + }, + "user": { + "display_name": "Ragne", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B00000000-0000-0000-0000-000000000001%7D" + }, + "avatar": { + "href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/initials/EM-3.png" + }, + "html": { + "href": "https://bitbucket.org/%7B00000000-0000-0000-0000-000000000001%7D/" + } + }, + "type": "user", + "uuid": "{00000000-0000-0000-0000-000000000001}", + "account_id": "000000:00000000-0000-0000-0000-000000000001", + "nickname": "Ragne" + }, + "deleted": false, + "pending": false, + "type": "pullrequest_comment", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5/comments/498931784" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5/_/diff#comment-498931784" + } + }, + "pullrequest": { + "type": "pullrequest", + "id": 5, + "title": "for test", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5" + } + } + } + }, + { + "id": 498931802, + "created_on": "2024-05-07T12:21:48.737851+00:00", + "updated_on": "2024-05-07T12:21:48.737927+00:00", + "content": { + "type": "rendered", + "raw": "Ran Plan for 0 projects:", + "markup": "markdown", + "html": "

Ran Plan for 0 projects:

" + }, + "user": { + "display_name": "bb bot", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B600000000-0000-0000-0000-000000000000%7D" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/d5c4bac76953df92f47d1dea43fcdba0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FKB-4.png" + }, + "html": { + "href": "https://bitbucket.org/%7B600000000-0000-0000-0000-000000000000%7D/" + } + }, + "type": "user", + "uuid": "{600000000-0000-0000-0000-000000000000}", + "account_id": "00000000-0000-0000-0000-000000000000", + "nickname": "bb bot" + }, + "deleted": false, + "pending": false, + "type": "pullrequest_comment", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5/comments/498931802" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5/_/diff#comment-498931802" + } + }, + "pullrequest": { + "type": "pullrequest", + "id": 5, + "title": "for test", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5" + } + } + } + }, + { + "id": 498931882, + "created_on": "2024-05-07T12:22:01.870344+00:00", + "updated_on": "2024-05-07T12:22:01.870462+00:00", + "content": { + "type": "rendered", + "raw": "atlantis plan", + "markup": "markdown", + "html": "

atlantis plan

" + }, + "user": { + "display_name": "Ragne", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B00000000-0000-0000-0000-000000000001%7D" + }, + "avatar": { + "href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/initials/EM-3.png" + }, + "html": { + "href": "https://bitbucket.org/%7B00000000-0000-0000-0000-000000000001%7D/" + } + }, + "type": "user", + "uuid": "{00000000-0000-0000-0000-000000000001}", + "account_id": "000000:00000000-0000-0000-0000-000000000001", + "nickname": "Ragne" + }, + "deleted": false, + "pending": false, + "type": "pullrequest_comment", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5/comments/498931882" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5/_/diff#comment-498931882" + } + }, + "pullrequest": { + "type": "pullrequest", + "id": 5, + "title": "for test", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5" + } + } + } + }, + { + "id": 498931901, + "created_on": "2024-05-07T12:22:04.981415+00:00", + "updated_on": "2024-05-07T12:22:04.981490+00:00", + "content": { + "type": "rendered", + "raw": "Ran Plan for 0 projects:", + "markup": "markdown", + "html": "

Ran Plan for 0 projects:

" + }, + "user": { + "display_name": "bb bot", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B600000000-0000-0000-0000-000000000000%7D" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/d5c4bac76953df92f47d1dea43fcdba0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FKB-4.png" + }, + "html": { + "href": "https://bitbucket.org/%7B600000000-0000-0000-0000-000000000000%7D/" + } + }, + "type": "user", + "uuid": "{600000000-0000-0000-0000-000000000000}", + "account_id": "00000000-0000-0000-0000-000000000000", + "nickname": "bb bot" + }, + "deleted": false, + "pending": false, + "type": "pullrequest_comment", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5/comments/498931901" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5/_/diff#comment-498931901" + } + }, + "pullrequest": { + "type": "pullrequest", + "id": 5, + "title": "for test", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5" + } + } + } + }, + { + "id": 49893111, + "created_on": "2024-05-07T12:22:05.981415+00:00", + "updated_on": "2024-05-07T12:22:05.981490+00:00", + "content": { + "type": "rendered", + "raw": "Ran Apply for 0 projects:", + "markup": "markdown", + "html": "

Ran Apply for 0 projects:

" + }, + "user": { + "display_name": "bb bot", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B600000000-0000-0000-0000-000000000000%7D" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/d5c4bac76953df92f47d1dea43fcdba0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FKB-4.png" + }, + "html": { + "href": "https://bitbucket.org/%7B600000000-0000-0000-0000-000000000000%7D/" + } + }, + "type": "user", + "uuid": "{600000000-0000-0000-0000-000000000000}", + "account_id": "00000000-0000-0000-0000-000000000000", + "nickname": "bb bot" + }, + "deleted": false, + "pending": false, + "type": "pullrequest_comment", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5/comments/498931901" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5/_/diff#comment-498931901" + } + }, + "pullrequest": { + "type": "pullrequest", + "id": 5, + "title": "for test", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/5" + }, + "html": { + "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/5" + } + } + } + } + ], + "pagelen": 10, + "size": 4, + "page": 1 +} \ No newline at end of file diff --git a/server/events/vcs/bitbucketcloud/testdata/user.json b/server/events/vcs/bitbucketcloud/testdata/user.json new file mode 100644 index 0000000000..336f27832a --- /dev/null +++ b/server/events/vcs/bitbucketcloud/testdata/user.json @@ -0,0 +1,33 @@ +{ + "display_name": "bb bot", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B00000000-0000-0000-0000-000000000001%7D" + }, + "avatar": { + "href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/initials/RR-3.png" + }, + "repositories": { + "href": "https://api.bitbucket.org/2.0/repositories/%7B00000000-0000-0000-0000-000000000001%7D" + }, + "snippets": { + "href": "https://api.bitbucket.org/2.0/snippets/%7B00000000-0000-0000-0000-000000000001%7D" + }, + "html": { + "href": "https://bitbucket.org/%7B00000000-0000-0000-0000-000000000001%7D/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/workspaces/%7B00000000-0000-0000-0000-000000000001%7D/hooks" + } + }, + "created_on": "2024-02-01T12:08:46.355300+00:00", + "type": "user", + "uuid": "{00000000-0000-0000-0000-000000000001}", + "has_2fa_enabled": null, + "username": "bb-bot", + "is_staff": false, + "account_id": "000000:00000000-0000-0000-0000-000000000001", + "nickname": "bb bot", + "account_status": "active", + "location": null +} From 3d73c923cf0803ea85cf8c87141646fe04610978 Mon Sep 17 00:00:00 2001 From: Edvard Makhlin Date: Tue, 7 May 2024 14:56:39 +0200 Subject: [PATCH 3/6] Mention bitbucket and required access perms in the docs --- runatlantis.io/docs/access-credentials.md | 2 +- runatlantis.io/docs/server-configuration.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runatlantis.io/docs/access-credentials.md b/runatlantis.io/docs/access-credentials.md index 1e46c9de24..de04a5d45b 100644 --- a/runatlantis.io/docs/access-credentials.md +++ b/runatlantis.io/docs/access-credentials.md @@ -122,7 +122,7 @@ Since v0.22.3, a new permission for `Members` has been added, which is required ### Bitbucket Cloud (bitbucket.org) - Create an App Password by following [BitBucket Cloud: Create an app password](https://support.atlassian.com/bitbucket-cloud/docs/create-an-app-password/) - Label the password "atlantis" -- Select **Pull requests**: **Read** and **Write** so that Atlantis can read your pull requests and write comments to them +- Select **Pull requests**: **Read** and **Write** so that Atlantis can read your pull requests and write comments to them. If you want to enable "hide-prev-plan-comments" feature and thus delete old comments, please add **Account**: **Read** as well. - Record the access token ### Bitbucket Server (aka Stash) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 87892c3dd2..ba3da7d30a 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -698,7 +698,7 @@ and set `--autoplan-modules` to `false`. ATLANTIS_HIDE_PREV_PLAN_COMMENTS=true ``` Hide previous plan comments to declutter PRs. This is only supported in - GitHub and GitLab currently. This is not enabled by default. When using Github App, you need to set `--gh-app-slug` to enable this feature. + GitHub, GitLab and Bitbucket currently. This is not enabled by default. When using Github App, you need to set `--gh-app-slug` to enable this feature. ### `--hide-unchanged-plan-comments` ```bash From 7daa4cfd1ad33413d172cfc5727c4d60b07d8689 Mon Sep 17 00:00:00 2001 From: Edvard Makhlin Date: Tue, 7 May 2024 15:03:33 +0200 Subject: [PATCH 4/6] Is that good enough memoization? --- server/events/vcs/bitbucketcloud/client.go | 40 ++++++++++++------- .../events/vcs/bitbucketcloud/client_test.go | 11 +++-- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/server/events/vcs/bitbucketcloud/client.go b/server/events/vcs/bitbucketcloud/client.go index df337812ef..a99c10c002 100644 --- a/server/events/vcs/bitbucketcloud/client.go +++ b/server/events/vcs/bitbucketcloud/client.go @@ -40,6 +40,8 @@ func NewClient(httpClient *http.Client, username string, password string, atlant } } +var MY_UUID = "" + // GetModifiedFiles returns the names of files that were modified in the merge request // relative to the repo root, e.g. parent/child/file.txt. func (b *Client) GetModifiedFiles(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest) ([]string, error) { @@ -134,7 +136,10 @@ func (b *Client) HidePrevCommandComments(logger logging.SimpleLogging, repo mode if strings.Contains(firstLine, strings.ToLower(command)) { // we found our old comment that references that command logger.Debug("Deleting comment with id %s", *c.ID) - b.DeletePullRequestComment(repo, pullNum, *c.ID) + err = b.DeletePullRequestComment(repo, pullNum, *c.ID) + if err != nil { + return err + } } } } @@ -165,25 +170,30 @@ func (b *Client) GetPullRequestComments(repo models.Repo, pullNum int) (comments } func (b *Client) GetMyUUID() (uuid string, err error) { - path := fmt.Sprintf("%s/2.0/user", b.BaseURL) - resp, err := b.makeRequest("GET", path, nil) + if MY_UUID == "" { + path := fmt.Sprintf("%s/2.0/user", b.BaseURL) + resp, err := b.makeRequest("GET", path, nil) - if err != nil { - return uuid, err - } + if err != nil { + return uuid, err + } - var user User - if err := json.Unmarshal(resp, &user); err != nil { - return uuid, errors.Wrapf(err, "Could not parse response %q", string(resp)) - } + var user User + if err := json.Unmarshal(resp, &user); err != nil { + return uuid, errors.Wrapf(err, "Could not parse response %q", string(resp)) + } - if err := validator.New().Struct(user); err != nil { - return uuid, errors.Wrapf(err, "API response %q was missing a field", string(resp)) - } + if err := validator.New().Struct(user); err != nil { + return uuid, errors.Wrapf(err, "API response %q was missing a field", string(resp)) + } - uuid = *user.UUID - return uuid, nil + uuid = *user.UUID + MY_UUID = uuid + return uuid, nil + } else { + return MY_UUID, nil + } } // PullIsApproved returns true if the merge request was approved. diff --git a/server/events/vcs/bitbucketcloud/client_test.go b/server/events/vcs/bitbucketcloud/client_test.go index 833e022d11..d3832f09e8 100644 --- a/server/events/vcs/bitbucketcloud/client_test.go +++ b/server/events/vcs/bitbucketcloud/client_test.go @@ -374,7 +374,7 @@ func TestClient_GetMyUUID(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { case "/2.0/user": - w.Write([]byte(json)) // nolint: errcheck + w.Write(json) // nolint: errcheck return default: t.Errorf("got unexpected request at %q", r.RequestURI) @@ -397,7 +397,7 @@ func TestClient_GetComment(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments": - w.Write([]byte(json)) // nolint: errcheck + w.Write(json) // nolint: errcheck return default: t.Errorf("got unexpected request at %q", r.RequestURI) @@ -472,7 +472,7 @@ func TestClient_HidePRComments(t *testing.T) { // we have two comments in the test file // The code is going to delete them all and then create a new one case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/498931882": - w.Write([]byte("")) + w.Write([]byte("")) // nolint: errcheck called += 1 return // This is the second one @@ -484,10 +484,10 @@ func TestClient_HidePRComments(t *testing.T) { Assert(t, r.Method != "DELETE", "Shouldn't delete this one") return case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments": - w.Write([]byte(comments)) // nolint: errcheck + w.Write(comments) // nolint: errcheck return case "/2.0/user": - w.Write([]byte(json)) // nolint: errcheck + w.Write(json) // nolint: errcheck return default: t.Errorf("got unexpected request at %q", r.RequestURI) @@ -514,4 +514,3 @@ func TestClient_HidePRComments(t *testing.T) { Ok(t, err) Equals(t, 2, called) } - From 32636d0e988eac721fff6828fe4ece42d8ee2a24 Mon Sep 17 00:00:00 2001 From: Edvard Makhlin Date: Thu, 23 May 2024 14:09:58 +0200 Subject: [PATCH 5/6] fix: Treat http no content as success --- server/events/vcs/bitbucketcloud/client.go | 2 +- server/events/vcs/bitbucketcloud/client_test.go | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/server/events/vcs/bitbucketcloud/client.go b/server/events/vcs/bitbucketcloud/client.go index a99c10c002..0d257da084 100644 --- a/server/events/vcs/bitbucketcloud/client.go +++ b/server/events/vcs/bitbucketcloud/client.go @@ -337,7 +337,7 @@ func (b *Client) makeRequest(method string, path string, reqBody io.Reader) ([]b defer resp.Body.Close() // nolint: errcheck requestStr := fmt.Sprintf("%s %s", method, path) - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent { respBody, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("making request %q unexpected status code: %d, body: %s", requestStr, resp.StatusCode, string(respBody)) } diff --git a/server/events/vcs/bitbucketcloud/client_test.go b/server/events/vcs/bitbucketcloud/client_test.go index d3832f09e8..eb7b19ecbb 100644 --- a/server/events/vcs/bitbucketcloud/client_test.go +++ b/server/events/vcs/bitbucketcloud/client_test.go @@ -431,7 +431,9 @@ func TestClient_DeleteComment(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/1": - w.Write([]byte("")) // nolint: errcheck + if r.Method == "DELETE" { + w.WriteHeader(http.StatusNoContent) + } return default: t.Errorf("got unexpected request at %q", r.RequestURI) @@ -472,11 +474,17 @@ func TestClient_HidePRComments(t *testing.T) { // we have two comments in the test file // The code is going to delete them all and then create a new one case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/498931882": + if r.Method == "DELETE" { + w.WriteHeader(http.StatusNoContent) + } w.Write([]byte("")) // nolint: errcheck called += 1 return // This is the second one case "/2.0/repositories/myorg/myrepo/pullrequests/5/comments/498931784": + if r.Method == "DELETE" { + http.Error(w, "", http.StatusNoContent) + } w.Write([]byte("")) // nolint: errcheck called += 1 return From 6dc3a6d07d903be149bc5f8a576d98015f56629c Mon Sep 17 00:00:00 2001 From: PePe Amengual <2208324+jamengual@users.noreply.github.com> Date: Mon, 30 Dec 2024 19:40:13 -0800 Subject: [PATCH 6/6] Update server/events/vcs/bitbucketcloud/testdata/comments.json Co-authored-by: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Signed-off-by: PePe Amengual <2208324+jamengual@users.noreply.github.com> --- server/events/vcs/bitbucketcloud/testdata/comments.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/events/vcs/bitbucketcloud/testdata/comments.json b/server/events/vcs/bitbucketcloud/testdata/comments.json index a697b1ab00..746accc259 100644 --- a/server/events/vcs/bitbucketcloud/testdata/comments.json +++ b/server/events/vcs/bitbucketcloud/testdata/comments.json @@ -269,4 +269,4 @@ "pagelen": 10, "size": 4, "page": 1 -} \ No newline at end of file +}