Repository: xanzy/go-gitlab
Pull request #1155 (2021-05-26 20:14:25)

Avatar support
Adding gitlab avatar support https://docs.gitlab.com/ee/api/avatar.html


Patch:
diff --git a/avatar.go b/avatar.go
new file mode 100644
index 0000000..1a7b923
--- /dev/null
+++ b/avatar.go
@@ -0,0 +1,64 @@
+//
+// Copyright 2021, Pavel Kostohrys
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package gitlab
+
+import (
+	"net/http"
+)
+
+// AvatarRequestsService handles communication with the avatar related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/avatar.html
+type AvatarRequestsService struct {
+	client *Client
+}
+
+// Avatar represents a GitLab avatar.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/avatar.html
+type Avatar struct {
+	AvatarURL string `json:"avatar_url"`
+}
+
+// GetAvatarOptions represents the available GetAvatar() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/avatar.html#get-a-single-avatar-url
+type GetAvatarOptions struct {
+	Email *string `url:"email,omitempty" json:"email,omitempty"`
+	Size  *int    `url:"size,omitempty" json:"size,omitempty"`
+}
+
+// GetAvatar gets the avatar URL for a user with the given email address.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/avatar.html#get-a-single-avatar-url
+func (s *AvatarRequestsService) GetAvatar(opt *GetAvatarOptions, options ...RequestOptionFunc) (*Avatar, *Response, error) {
+	req, err := s.client.NewRequest(http.MethodGet, "avatar", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	avatar := new(Avatar)
+	response, err := s.client.Do(req, avatar)
+	if err != nil {
+		return nil, response, err
+	}
+
+	return avatar, response, nil
+}
diff --git a/boards.go b/boards.go
index cec747a..ef85023 100644
--- a/boards.go
+++ b/boards.go
@@ -61,7 +61,7 @@ func (b BoardList) String() string {
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-starter
 type CreateIssueBoardOptions struct {
-	Name *string `url:"name" json:"name"`
+	Name *string `url:"name,omitempty" json:"name,omitempty"`
 }
 
 // CreateIssueBoard creates a new issue board.
diff --git a/event_webhook_types.go b/event_webhook_types.go
index fb89ade..f59b831 100644
--- a/event_webhook_types.go
+++ b/event_webhook_types.go
@@ -530,28 +530,27 @@ type MergeEvent struct {
 				Email string `json:"email"`
 			} `json:"author"`
 		} `json:"last_commit"`
-		WorkInProgress bool           `json:"work_in_progress"`
-		URL            string         `json:"url"`
-		Action         string         `json:"action"`
-		OldRev         string         `json:"oldrev"`
-		Assignee       *MergeAssignee `json:"assignee"`
+		WorkInProgress bool          `json:"work_in_progress"`
+		URL            string        `json:"url"`
+		Action         string        `json:"action"`
+		OldRev         string        `json:"oldrev"`
+		Assignee       MergeAssignee `json:"assignee"`
 	} `json:"object_attributes"`
-	Repository *Repository      `json:"repository"`
-	Assignee   *MergeAssignee   `json:"assignee"`
-	Assignees  []*MergeAssignee `json:"assignees"`
-	Labels     []*Label         `json:"labels"`
+	Repository *Repository   `json:"repository"`
+	Assignee   MergeAssignee `json:"assignee"`
+	Labels     []Label       `json:"labels"`
 	Changes    struct {
 		Assignees struct {
-			Previous []*MergeAssignee `json:"previous"`
-			Current  []*MergeAssignee `json:"current"`
+			Previous []MergeAssignee `json:"previous"`
+			Current  []MergeAssignee `json:"current"`
 		} `json:"assignees"`
 		Description struct {
 			Previous string `json:"previous"`
 			Current  string `json:"current"`
 		} `json:"description"`
 		Labels struct {
-			Previous []*Label `json:"previous"`
-			Current  []*Label `json:"current"`
+			Previous []Label `json:"previous"`
+			Current  []Label `json:"current"`
 		} `json:"labels"`
 		SourceBranch struct {
 			Previous string `json:"previous"`
@@ -590,11 +589,9 @@ type MergeEvent struct {
 
 // MergeAssignee represents a merge assignee.
 type MergeAssignee struct {
-	ID        int    `json:"id"`
 	Name      string `json:"name"`
 	Username  string `json:"username"`
 	AvatarURL string `json:"avatar_url"`
-	Email     string `json:"email"`
 }
 
 // MergeParams represents the merge params.
diff --git a/gitlab.go b/gitlab.go
index feed610..ecd18fc 100644
--- a/gitlab.go
+++ b/gitlab.go
@@ -101,6 +101,7 @@ type Client struct {
 	AccessRequests        *AccessRequestsService
 	Applications          *ApplicationsService
 	AuditEvents           *AuditEventsService
+	Avatar                *AvatarRequestsService
 	AwardEmoji            *AwardEmojiService
 	Boards                *IssueBoardsService
 	Branches              *BranchesService
@@ -274,6 +275,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
 	c.AccessRequests = &AccessRequestsService{client: c}
 	c.Applications = &ApplicationsService{client: c}
 	c.AuditEvents = &AuditEventsService{client: c}
+	c.Avatar = &AvatarRequestsService{client: c}
 	c.AwardEmoji = &AwardEmojiService{client: c}
 	c.Boards = &IssueBoardsService{client: c}
 	c.Branches = &BranchesService{client: c}


Test patch:
diff --git a/avatar_test.go b/avatar_test.go
new file mode 100644
index 0000000..4371e76
--- /dev/null
+++ b/avatar_test.go
@@ -0,0 +1,54 @@
+//
+// Copyright 2021, Pavel Kostohrys
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package gitlab
+
+import (
+	"encoding/json"
+	"net/http"
+	"testing"
+)
+
+func TestGetAvatar(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	const url = "https://www.gravatar.com/avatar/10e6bf7bcf22c2f00a3ef684b4ada178"
+
+	mux.HandleFunc("/api/v4/avatar",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodGet)
+			w.WriteHeader(http.StatusAccepted)
+			avatar := Avatar{AvatarURL: url}
+			resp, _ := json.Marshal(avatar)
+			_, _ = w.Write(resp)
+		},
+	)
+
+	opt := &GetAvatarOptions{Email: String("sander@vanharmelen.nnl")}
+	avatar, resp, err := client.Avatar.GetAvatar(opt)
+	if err != nil {
+		t.Fatalf("Avatar.GetAvatar returned error: %v", err)
+	}
+
+	if resp.Status != "202 Accepted" {
+		t.Fatalf("Avatar.GetAvatar returned wrong status code: %v", resp.Status)
+	}
+
+	if url != avatar.AvatarURL {
+		t.Errorf("Avatar.GetAvatar wrong result %s, want %s", avatar.AvatarURL, url)
+	}
+}
diff --git a/event_webhook_types_test.go b/event_webhook_types_test.go
index 6d70b75..71b98f8 100644
--- a/event_webhook_types_test.go
+++ b/event_webhook_types_test.go
@@ -21,11 +21,6 @@ import (
 	"testing"
 )
 
-const (
-	expectedName     = "User1"
-	expectedUsername = "user1"
-)
-
 func TestBuildEventUnmarshal(t *testing.T) {
 	jsonObject := loadFixture("testdata/webhooks/build.json")
 
@@ -148,12 +143,12 @@ func TestMergeEventUnmarshal(t *testing.T) {
 	if event.ObjectAttributes.LastCommit.ID != "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" {
 		t.Errorf("ObjectAttributes.LastCommit.ID is %v, want %s", event.ObjectAttributes.LastCommit.ID, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7")
 	}
-	if event.ObjectAttributes.Assignee.Name != expectedName {
-		t.Errorf("Assignee.Name is %v, want %v", event.ObjectAttributes.Assignee.Name, expectedName)
+	if event.ObjectAttributes.Assignee.Name != "User1" {
+		t.Errorf("Assignee.Name is %v, want %v", event.ObjectAttributes.Assignee.Name, "User1")
 	}
 
-	if event.ObjectAttributes.Assignee.Username != expectedUsername {
-		t.Errorf("ObjectAttributes is %v, want %v", event.ObjectAttributes.Assignee.Username, expectedUsername)
+	if event.ObjectAttributes.Assignee.Username != "user1" {
+		t.Errorf("ObjectAttributes is %v, want %v", event.ObjectAttributes.Assignee.Username, "user1")
 	}
 
 	if event.User.Name != "Administrator" {
@@ -167,18 +162,6 @@ func TestMergeEventUnmarshal(t *testing.T) {
 	if name := event.ObjectAttributes.LastCommit.Author.Name; name != "GitLab dev user" {
 		t.Errorf("Commit Username is %s, want %s", name, "GitLab dev user")
 	}
-
-	if event.Assignees[0].ID != 1 {
-		t.Errorf("Assignees[0].ID is %v, want %v", event.Assignees[0].ID, 1)
-	}
-
-	if event.Assignees[0].Name != expectedName {
-		t.Errorf("Assignees[0].Name is %v, want %v", event.Assignees[0].Name, expectedName)
-	}
-
-	if event.Assignees[0].Username != expectedUsername {
-		t.Errorf("Assignees[0].Username is %v, want %v", event.Assignees[0].Username, expectedName)
-	}
 }
 
 func TestMergeEventUnmarshalFromGroup(t *testing.T) {
diff --git a/testdata/webhooks/merge_request.json b/testdata/webhooks/merge_request.json
index e416f26..6678953 100644
--- a/testdata/webhooks/merge_request.json
+++ b/testdata/webhooks/merge_request.json
@@ -95,14 +95,6 @@
       "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
     }
   },
-  "assignees": [
-    {
-      "id": 1,
-      "name": "User1",
-      "username": "user1",
-      "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
-    }
-  ],
   "labels": [{
     "id": 206,
     "title": "API",