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

Fully support project and group avatars #1506

Merged
merged 2 commits into from Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 45 additions & 2 deletions groups.go
Expand Up @@ -18,10 +18,13 @@ package gitlab

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"

retryablehttp "github.com/hashicorp/go-retryablehttp"
)

// GroupsService handles communication with the group related methods of
Expand Down Expand Up @@ -90,6 +93,15 @@ type GroupAvatar struct {
Image io.Reader
}

// MarshalJSON implements the json.Marshaler interface.
func (a *GroupAvatar) MarshalJSON() ([]byte, error) {
if a.Filename == "" && a.Image == nil {
return []byte(`""`), nil
}
type alias GroupAvatar
return json.Marshal((*alias)(a))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why this is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wouldn't have this the field wouldn't be sent. In the current implementation it will send an empty string which effectively means to remove the avatar, e.g.:

curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22" \
     --data "avatar="


// LDAPGroupLink represents a GitLab LDAP group link.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#ldap-group-links
Expand Down Expand Up @@ -312,6 +324,7 @@ func (s *GroupsService) DownloadAvatar(gid interface{}, options ...RequestOption
type CreateGroupOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
Path *string `url:"path,omitempty" json:"path,omitempty"`
Avatar *GroupAvatar `url:"-" json:"-"`
Description *string `url:"description,omitempty" json:"description,omitempty"`
MembershipLock *bool `url:"membership_lock,omitempty" json:"membership_lock,omitempty"`
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
Expand All @@ -336,7 +349,22 @@ type CreateGroupOptions struct {
//
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
req, err := s.client.NewRequest(http.MethodPost, "groups", opt, options)
var err error
var req *retryablehttp.Request

if opt.Avatar == nil {
req, err = s.client.NewRequest(http.MethodPost, "groups", opt, options)
} else {
req, err = s.client.UploadRequest(
http.MethodPost,
"groups",
opt.Avatar.Image,
opt.Avatar.Filename,
UploadAvatar,
opt,
options,
)
}
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -420,6 +448,7 @@ func (s *GroupsService) TransferSubGroup(gid interface{}, opt *TransferSubGroupO
type UpdateGroupOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
Path *string `url:"path,omitempty" json:"path,omitempty"`
Avatar *GroupAvatar `url:"-" json:"avatar,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be:

Suggested change
Avatar *GroupAvatar `url:"-" json:"avatar,omitempty"`
Avatar *GroupAvatar `url:"-" json:"-"`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm, good question. I think in case of UploadRequest() the options are any ways never JSON encoded, but just form fields ... So, probably it doesn't matter. I have to double check.

Description *string `url:"description,omitempty" json:"description,omitempty"`
MembershipLock *bool `url:"membership_lock,omitempty" json:"membership_lock,omitempty"`
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
Expand Down Expand Up @@ -453,7 +482,21 @@ func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, op
}
u := fmt.Sprintf("groups/%s", PathEscape(group))

req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
var req *retryablehttp.Request

if opt.Avatar == nil || (opt.Avatar.Filename == "" && opt.Avatar.Image == nil) {
req, err = s.client.NewRequest(http.MethodPut, u, opt, options)
} else {
req, err = s.client.UploadRequest(
http.MethodPut,
u,
opt.Avatar.Image,
opt.Avatar.Filename,
UploadAvatar,
opt,
options,
)
}
if err != nil {
return nil, nil, err
}
Expand Down
16 changes: 13 additions & 3 deletions projects.go
Expand Up @@ -17,6 +17,7 @@
package gitlab

import (
"encoding/json"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -704,6 +705,15 @@ type ProjectAvatar struct {
Image io.Reader
}

// MarshalJSON implements the json.Marshaler interface.
func (a *ProjectAvatar) MarshalJSON() ([]byte, error) {
if a.Filename == "" && a.Image == nil {
return []byte(`""`), nil
}
type alias ProjectAvatar
return json.Marshal((*alias)(a))
}

// CreateProject creates a new project owned by the authenticated user.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#create-project
Expand Down Expand Up @@ -805,7 +815,7 @@ type EditProjectOptions struct {
AutoDevopsDeployStrategy *string `url:"auto_devops_deploy_strategy,omitempty" json:"auto_devops_deploy_strategy,omitempty"`
AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"`
AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"`
Avatar *ProjectAvatar `url:"-" json:"-"`
Avatar *ProjectAvatar `url:"-" json:"avatar,omitempty"`
BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"`
BuildGitStrategy *string `url:"build_git_strategy,omitempty" json:"build_git_strategy,omitempty"`
BuildTimeout *int `url:"build_timeout,omitempty" json:"build_timeout,omitempty"`
Expand Down Expand Up @@ -893,11 +903,11 @@ func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions,

var req *retryablehttp.Request

if opt.Avatar == nil {
if opt.Avatar == nil || (opt.Avatar.Filename == "" && opt.Avatar.Image == nil) {
req, err = s.client.NewRequest(http.MethodPut, u, opt, options)
} else {
req, err = s.client.UploadRequest(
http.MethodPost,
http.MethodPut,
u,
opt.Avatar.Image,
opt.Avatar.Filename,
Expand Down