From ded2ca54b521dfc1af7fc3a18d894e4e7beb57b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Sarzy=C5=84ski?= Date: Mon, 4 Jul 2022 11:06:39 +0200 Subject: [PATCH 1/3] Fix types for robot comment suggestions --- changes.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/changes.go b/changes.go index 797deee..fdcfbd1 100644 --- a/changes.go +++ b/changes.go @@ -295,7 +295,7 @@ type RobotCommentInput struct { Properties *map[string]*string `json:"properties,omitempty"` // Suggested fixes for this robot comment as a list of FixSuggestionInfo // entities. - FixSuggestions *FixSuggestionInfo `json:"fix_suggestions,omitempty"` + FixSuggestions []FixSuggestionInfo `json:"fix_suggestions,omitempty"` } // RobotCommentInfo entity contains information about a robot inline comment @@ -314,7 +314,7 @@ type RobotCommentInfo struct { Properties map[string]string `json:"properties,omitempty"` // Suggested fixes for this robot comment as a list of FixSuggestionInfo // entities. - FixSuggestions *FixSuggestionInfo `json:"fix_suggestions,omitempty"` + FixSuggestions []FixSuggestionInfo `json:"fix_suggestions,omitempty"` } // FixSuggestionInfo entity represents a suggested fix. @@ -328,7 +328,7 @@ type FixSuggestionInfo struct { // A list of FixReplacementInfo entities indicating how the content of one or // several files should be modified. Within a file, they should refer to // non-overlapping regions. - Replacements FixReplacementInfo `json:"replacements"` + Replacements []FixReplacementInfo `json:"replacements"` } // FixReplacementInfo entity describes how the content of a file should be replaced by another content. @@ -345,7 +345,7 @@ type FixReplacementInfo struct { Range CommentRange `json:"range"` // The content which should be used instead of the current one. - Replacement string `json:"replacement,omitempty"` + Replacement string `json:"replacement"` } // AttentionSetInfo entity contains details of users that are in the attention set. From c0357e304f03cc13578cf2d7fbfa9381d44e8216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Sarzy=C5=84ski?= Date: Wed, 6 Jul 2022 14:39:39 +0200 Subject: [PATCH 2/3] Add API endpoints related to robot comments --- changes.go | 25 +++++ changes_revision.go | 25 +++++ changes_revision_test.go | 233 +++++++++++++++++++++++++++++++++++++++ changes_test.go | 122 ++++++++++++++++++++ 4 files changed, 405 insertions(+) diff --git a/changes.go b/changes.go index fdcfbd1..c39eaf7 100644 --- a/changes.go +++ b/changes.go @@ -673,6 +673,15 @@ func (s *ChangesService) ListChangeComments(changeID string) (*map[string][]Comm return s.getCommentInfoMapResponse(u) } +// Lists the robot comments of all revisions of the change. +// Return a map that maps the file path to a list of RobotCommentInfo entries. The entries in the map are sorted by file path. +// +// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-change-robot-comments +func (s *ChangesService) ListChangeRobotComments(changeID string) (map[string][]RobotCommentInfo, *Response, error) { + u := fmt.Sprintf("changes/%s/robotcomments", changeID) + return s.getRobotCommentInfoMapSliceResponse(u) +} + // ListChangeDrafts lLists the draft comments of all revisions of the change that belong to the calling user. // The entries in the map are sorted by file path, and the comments for each path are sorted by patch set number. // Each comment has the patch_set field set, and no author. @@ -741,6 +750,22 @@ func (s *ChangesService) getCommentInfoMapSliceResponse(u string) (*map[string][ return v, resp, err } +// getRobotCommentInfoMapSliceResponse retrieved a map with a slice of RobotCommentInfo Response for a GET request +func (s *ChangesService) getRobotCommentInfoMapSliceResponse(u string) (map[string][]RobotCommentInfo, *Response, error) { + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var v map[string][]RobotCommentInfo + resp, err := s.client.Do(req, &v) + if err != nil { + return nil, resp, err + } + + return v, resp, err +} + // CreateChange creates a new change. // // The change input ChangeInput entity must be provided in the request body. diff --git a/changes_revision.go b/changes_revision.go index 2a39c9f..13d5d21 100644 --- a/changes_revision.go +++ b/changes_revision.go @@ -201,6 +201,26 @@ func (s *ChangesService) GetComment(changeID, revisionID, commentID string) (*Co return s.getCommentInfoResponse(u) } +// GetRobotComment retrieves a robot comment of a revision. +// +// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-robot-comment +func (s *ChangesService) GetRobotComment(changeID, revisionID, commentID string) (*RobotCommentInfo, *Response, error) { + u := fmt.Sprintf("changes/%s/revisions/%s/robotcomments/%s", changeID, revisionID, commentID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + v := new(RobotCommentInfo) + resp, err := s.client.Do(req, v) + if err != nil { + return nil, resp, err + } + + return v, resp, err +} + // GetSubmitType gets the method the server will use to submit (merge) the change. // // Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-submit-type @@ -313,6 +333,11 @@ func (s *ChangesService) ListRevisionComments(changeID, revisionID string) (*map return s.getCommentInfoMapSliceResponse(u) } +func (s *ChangesService) ListRevisionRobotComments(changeID, revisionID string) (map[string][]RobotCommentInfo, *Response, error) { + u := fmt.Sprintf("changes/%s/revisions/%s/robotcomments", changeID, revisionID) + return s.getRobotCommentInfoMapSliceResponse(u) +} + // ListFiles lists the files that were modified, added or deleted in a revision. // As result a map is returned that maps the file path to a list of FileInfo entries. // The entries in the map are sorted by file path. diff --git a/changes_revision_test.go b/changes_revision_test.go index cccfd3b..7688fff 100644 --- a/changes_revision_test.go +++ b/changes_revision_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "reflect" "testing" + "time" "github.com/andygrunwald/go-gerrit" ) @@ -79,3 +80,235 @@ func TestChangesService_ListFilesReviewed(t *testing.T) { t.Errorf("client.Changes.ListFilesReviewed:\ngot: %q\nwant: %q", got, want) } } + +func TestChangesService_ListRevisionRobotComment(t *testing.T) { + listRobotCommentResponse := `)]}' + { + "main.c": [ + { + "robot_id": "robot", + "robot_run_id": "1", + "fix_suggestions": [ + { + "fix_id": "c3302a6f_1578ee9e", + "description": "suggestion", + "replacements": [ + { + "path": "main.c", + "range": { + "start_line": 3, + "start_character": 0, + "end_line": 5, + "end_character": 1 + }, + "replacement": "int main() { printf(\"Hello world!\"); }" + } + ] + } + ], + "author": { + "_account_id": 1000000, + "name": "Jhon Smith", + "username": "jhon" + }, + "change_message_id": "517e6c92e4bd105f1e611294a3010ea177771551", + "patch_set": 1, + "id": "f7d3d07f_bf17b66e", + "line": 5, + "range": { + "start_line": 3, + "start_character": 0, + "end_line": 5, + "end_character": 1 + }, + "updated": "2022-07-05 13:44:34.000000000", + "message": "[clang-format] fix suggestion", + "commit_id": "be8ce493368f2ce0fa73b56f5e2bd0dc17ca4359" + } + ] + }` + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/changes/changeID/revisions/revisionID/robotcomments" { + t.Errorf("%s != /changes/changeID/revisions/revisionID/robotcomments", r.URL.Path) + } + if r.Method != "GET" { + t.Error("Method != GET") + } + fmt.Fprint(w, listRobotCommentResponse) + })) + defer ts.Close() + + client, err := gerrit.NewClient(ts.URL, nil) + if err != nil { + t.Error(err) + } + + got, _, err := client.Changes.ListRevisionRobotComments("changeID", "revisionID") + if err != nil { + t.Errorf("Changes.ListRevisionRobotComments returned error: %v", err) + } + + want := map[string][]gerrit.RobotCommentInfo{ + "main.c": { + { + CommentInfo: gerrit.CommentInfo{ + PatchSet: 1, + ID: "f7d3d07f_bf17b66e", + Line: 5, + Range: &gerrit.CommentRange{ + StartLine: 3, + StartCharacter: 0, + EndLine: 5, + EndCharacter: 1, + }, + Message: "[clang-format] fix suggestion", + Updated: &gerrit.Timestamp{ + Time: time.Date(2022, 7, 5, 13, 44, 34, 0, time.UTC), + }, + Author: gerrit.AccountInfo{ + AccountID: 1000000, + Name: "Jhon Smith", + Username: "jhon", + }, + }, + RobotID: "robot", + RobotRunID: "1", + FixSuggestions: []gerrit.FixSuggestionInfo{ + { + FixID: "c3302a6f_1578ee9e", + Description: "suggestion", + Replacements: []gerrit.FixReplacementInfo{ + { + Path: "main.c", + Range: gerrit.CommentRange{ + StartLine: 3, + StartCharacter: 0, + EndLine: 5, + EndCharacter: 1, + }, + Replacement: "int main() { printf(\"Hello world!\"); }", + }, + }, + }, + }, + }, + }, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Change.ListRevisionRobotComments:\ngot: %+v\nwant: %+v", got, want) + } +} + +func TestChangesService_GetRobotComment(t *testing.T) { + getRobotCommentResponse := `)]}' + { + "robot_id": "robot", + "robot_run_id": "1", + "fix_suggestions": [ + { + "fix_id": "c3302a6f_1578ee9e", + "description": "suggestion", + "replacements": [ + { + "path": "main.c", + "range": { + "start_line": 3, + "start_character": 0, + "end_line": 5, + "end_character": 1 + }, + "replacement": "int main() { printf(\"Hello world!\"); }" + } + ] + } + ], + "author": { + "_account_id": 1000000, + "name": "Jhon Smith", + "username": "jhon" + }, + "change_message_id": "517e6c92e4bd105f1e611294a3010ea177771551", + "patch_set": 1, + "id": "f7d3d07f_bf17b66e", + "line": 5, + "range": { + "start_line": 3, + "start_character": 0, + "end_line": 5, + "end_character": 1 + }, + "updated": "2022-07-05 13:44:34.000000000", + "message": "[clang-format] fix suggestion", + "commit_id": "be8ce493368f2ce0fa73b56f5e2bd0dc17ca4359" + }` + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/changes/changeID/revisions/revisionID/robotcomments/commentID" { + t.Errorf("%s != /changes/changeID/revisions/revisionID/robotcomments/commentID", r.URL.Path) + } + if r.Method != "GET" { + t.Error("Method != GET") + } + fmt.Fprint(w, getRobotCommentResponse) + })) + defer ts.Close() + + client, err := gerrit.NewClient(ts.URL, nil) + if err != nil { + t.Error(err) + } + + got, _, err := client.Changes.GetRobotComment("changeID", "revisionID", "commentID") + if err != nil { + t.Errorf("Changes.GetRobotComment returned error: %v", err) + } + + want := &gerrit.RobotCommentInfo{ + CommentInfo: gerrit.CommentInfo{ + PatchSet: 1, + ID: "f7d3d07f_bf17b66e", + Line: 5, + Range: &gerrit.CommentRange{ + StartLine: 3, + StartCharacter: 0, + EndLine: 5, + EndCharacter: 1, + }, + Message: "[clang-format] fix suggestion", + Updated: &gerrit.Timestamp{ + Time: time.Date(2022, 7, 5, 13, 44, 34, 0, time.UTC), + }, + Author: gerrit.AccountInfo{ + AccountID: 1000000, + Name: "Jhon Smith", + Username: "jhon", + }, + }, + RobotID: "robot", + RobotRunID: "1", + FixSuggestions: []gerrit.FixSuggestionInfo{ + { + FixID: "c3302a6f_1578ee9e", + Description: "suggestion", + Replacements: []gerrit.FixReplacementInfo{ + { + Path: "main.c", + Range: gerrit.CommentRange{ + StartLine: 3, + StartCharacter: 0, + EndLine: 5, + EndCharacter: 1, + }, + Replacement: "int main() { printf(\"Hello world!\"); }", + }, + }, + }, + }, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Change.GetRobotComment:\ngot: %+v\nwant: %+v", got, want) + } +} diff --git a/changes_test.go b/changes_test.go index df10984..edc7d91 100644 --- a/changes_test.go +++ b/changes_test.go @@ -5,7 +5,9 @@ import ( "fmt" "net/http" "net/http/httptest" + "reflect" "testing" + "time" "github.com/andygrunwald/go-gerrit" ) @@ -499,3 +501,123 @@ func TestChangesService_SetReadyForReview_NotFound(t *testing.T) { t.Error("Expected 404 code") } } + +func TestChangesService_ListChangeRobotComment(t *testing.T) { + listRobotCommentResponse := `)]}' + { + "main.c": [ + { + "robot_id": "robot", + "robot_run_id": "1", + "fix_suggestions": [ + { + "fix_id": "c3302a6f_1578ee9e", + "description": "suggestion", + "replacements": [ + { + "path": "main.c", + "range": { + "start_line": 3, + "start_character": 0, + "end_line": 5, + "end_character": 1 + }, + "replacement": "int main() { printf(\"Hello world!\"); }" + } + ] + } + ], + "author": { + "_account_id": 1000000, + "name": "Jhon Smith", + "username": "jhon" + }, + "change_message_id": "517e6c92e4bd105f1e611294a3010ea177771551", + "patch_set": 1, + "id": "f7d3d07f_bf17b66e", + "line": 5, + "range": { + "start_line": 3, + "start_character": 0, + "end_line": 5, + "end_character": 1 + }, + "updated": "2022-07-05 13:44:34.000000000", + "message": "[clang-format] fix suggestion", + "commit_id": "be8ce493368f2ce0fa73b56f5e2bd0dc17ca4359" + } + ] + }` + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/changes/changeID/robotcomments" { + t.Errorf("%s != /changes/changeID/robotcomments", r.URL.Path) + } + if r.Method != "GET" { + t.Error("Method != GET") + } + fmt.Fprint(w, listRobotCommentResponse) + })) + defer ts.Close() + + client, err := gerrit.NewClient(ts.URL, nil) + if err != nil { + t.Error(err) + } + + got, _, err := client.Changes.ListChangeRobotComments("changeID") + if err != nil { + t.Errorf("Changes.ListChangeRobotComments returned error: %v", err) + } + + want := map[string][]gerrit.RobotCommentInfo{ + "main.c": { + { + CommentInfo: gerrit.CommentInfo{ + PatchSet: 1, + ID: "f7d3d07f_bf17b66e", + Line: 5, + Range: &gerrit.CommentRange{ + StartLine: 3, + StartCharacter: 0, + EndLine: 5, + EndCharacter: 1, + }, + Message: "[clang-format] fix suggestion", + Updated: &gerrit.Timestamp{ + Time: time.Date(2022, 7, 5, 13, 44, 34, 0, time.UTC), + }, + Author: gerrit.AccountInfo{ + AccountID: 1000000, + Name: "Jhon Smith", + Username: "jhon", + }, + }, + RobotID: "robot", + RobotRunID: "1", + FixSuggestions: []gerrit.FixSuggestionInfo{ + { + FixID: "c3302a6f_1578ee9e", + Description: "suggestion", + Replacements: []gerrit.FixReplacementInfo{ + { + Path: "main.c", + Range: gerrit.CommentRange{ + StartLine: 3, + StartCharacter: 0, + EndLine: 5, + EndCharacter: 1, + }, + Replacement: "int main() { printf(\"Hello world!\"); }", + }, + }, + }, + }, + }, + }, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Change.ListChangeRobotComments:\ngot: %+v\nwant: %+v", got, want) + } +} From cf145bf2ac5598a5c6af78f8f541cc09e4bfd3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Sarzy=C5=84ski?= Date: Wed, 6 Jul 2022 14:45:40 +0200 Subject: [PATCH 3/3] Remove duplicate function Function getCommentInfoMapResponse is equivalent to getCommentInfoMapSliceResponse. Keep the latter. --- changes.go | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/changes.go b/changes.go index c39eaf7..26789a3 100644 --- a/changes.go +++ b/changes.go @@ -670,7 +670,7 @@ func (s *ChangesService) GetIncludedIn(changeID string) (*IncludedInInfo, *Respo // Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-change-comments func (s *ChangesService) ListChangeComments(changeID string) (*map[string][]CommentInfo, *Response, error) { u := fmt.Sprintf("changes/%s/comments", changeID) - return s.getCommentInfoMapResponse(u) + return s.getCommentInfoMapSliceResponse(u) } // Lists the robot comments of all revisions of the change. @@ -689,23 +689,7 @@ func (s *ChangesService) ListChangeRobotComments(changeID string) (map[string][] // Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-change-drafts func (s *ChangesService) ListChangeDrafts(changeID string) (*map[string][]CommentInfo, *Response, error) { u := fmt.Sprintf("changes/%s/drafts", changeID) - return s.getCommentInfoMapResponse(u) -} - -// getCommentInfoMapResponse retrieved a map of CommentInfo Response for a GET request -func (s *ChangesService) getCommentInfoMapResponse(u string) (*map[string][]CommentInfo, *Response, error) { - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - v := new(map[string][]CommentInfo) - resp, err := s.client.Do(req, v) - if err != nil { - return nil, resp, err - } - - return v, resp, err + return s.getCommentInfoMapSliceResponse(u) } // CheckChange performs consistency checks on the change, and returns a ChangeInfo entity with the problems field set to a list of ProblemInfo entities.