Commit a8e82484 authored by Timo Furrer's avatar Timo Furrer
Browse files

Merge branch 'upload-do-requests' into 'main'

Support file uploads in `do()` request handler

See merge request !2732
parents 07cff51b dcf2af55
Loading
Loading
Loading
Loading
Loading
+7 −19
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
package gitlab

import (
	"fmt"
	"io"
	"net/http"
	"time"
@@ -83,24 +82,13 @@ type UploadMetricImageOptions struct {
}

func (s *AlertManagementService) UploadMetricImage(pid any, alertIID int64, content io.Reader, filename string, opt *UploadMetricImageOptions, options ...RequestOptionFunc) (*MetricImage, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/alert_management_alerts/%d/metric_images", PathEscape(project), alertIID)

	req, err := s.client.UploadRequest(http.MethodPost, u, content, filename, UploadFile, opt, options)
	if err != nil {
		return nil, nil, err
	}

	mi := new(MetricImage)
	resp, err := s.client.Do(req, mi)
	if err != nil {
		return nil, resp, err
	}

	return mi, resp, nil
	return do[*MetricImage](s.client,
		withMethod(http.MethodPost),
		withPath("projects/%s/alert_management_alerts/%d/metric_images", ProjectID{pid}, alertIID),
		withUpload(content, filename, UploadFile),
		withAPIOpts(opt),
		withRequestOpts(options...),
	)
}

// ListMetricImagesOptions represents the available ListMetricImages() options.
+22 −87
Original line number Diff line number Diff line
@@ -26,8 +26,6 @@ import (
	"net/url"
	"strconv"
	"time"

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

type (
@@ -479,38 +477,19 @@ func (d *DefaultBranchProtectionDefaultsOptions) EncodeValues(key string, v *url
//
// GitLab API docs: https://docs.gitlab.com/api/groups/#create-a-group
func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	var err error
	var req *retryablehttp.Request

	if opt.Avatar == nil {
		req, err = s.client.NewRequest(http.MethodPost, "groups", opt, options)
	} else {
		// since the Avatar is provided, check allowed_to_push and
		// allowed_to_merge access levels and error if multiples are provided
	reqOpts := []doOption{
		withMethod(http.MethodPost),
		withPath("groups"),
		withAPIOpts(opt),
		withRequestOpts(options...),
	}
	if opt.Avatar != nil {
		if opt.DefaultBranchProtectionDefaults != nil && (len(*opt.DefaultBranchProtectionDefaults.AllowedToMerge) > 1 || len(*opt.DefaultBranchProtectionDefaults.AllowedToPush) > 1) {
			return nil, nil, errors.New("multiple access levels for allowed_to_merge or allowed_to_push are not permitted when an Avatar is also specified as it will result in unexpected behavior")
		}
		req, err = s.client.UploadRequest(
			http.MethodPost,
			"groups",
			opt.Avatar.Image,
			opt.Avatar.Filename,
			UploadAvatar,
			opt,
			options,
		)
		reqOpts = append(reqOpts, withUpload(opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar))
	}
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
	return do[*Group](s.client, reqOpts...)
}

// TransferGroup transfers a project to the Group namespace. Available only
@@ -603,43 +582,19 @@ type UpdateGroupOptions struct {
//
// GitLab API docs: https://docs.gitlab.com/api/groups/#update-group-attributes
func (s *GroupsService) UpdateGroup(gid any, opt *UpdateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	reqOpts := []doOption{
		withMethod(http.MethodPut),
		withPath("groups/%s", GroupID{gid}),
		withAPIOpts(opt),
		withRequestOpts(options...),
	}
	u := fmt.Sprintf("groups/%s", PathEscape(group))

	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 {
		// since the Avatar is provided, check allowed_to_push and
		// allowed_to_merge access levels and error if multiples are provided
	if opt.Avatar != nil && (opt.Avatar.Filename != "" || opt.Avatar.Image != nil) {
		if opt.DefaultBranchProtectionDefaults != nil && (len(*opt.DefaultBranchProtectionDefaults.AllowedToMerge) > 1 || len(*opt.DefaultBranchProtectionDefaults.AllowedToPush) > 1) {
			return nil, nil, errors.New("multiple access levels for allowed_to_merge or allowed_to_push are not permitted when an Avatar is also specified as it will result in unexpected behavior")
		}
		req, err = s.client.UploadRequest(
			http.MethodPut,
			u,
			opt.Avatar.Image,
			opt.Avatar.Filename,
			UploadAvatar,
			opt,
			options,
		)
	}
	if err != nil {
		return nil, nil, err
		reqOpts = append(reqOpts, withUpload(opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar))
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
	return do[*Group](s.client, reqOpts...)
}

// UploadAvatar uploads a group avatar.
@@ -647,32 +602,12 @@ func (s *GroupsService) UpdateGroup(gid any, opt *UpdateGroupOptions, options ..
// GitLab API docs:
// https://docs.gitlab.com/api/groups/#upload-a-group-avatar
func (s *GroupsService) UploadAvatar(gid any, avatar io.Reader, filename string, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s", PathEscape(group))

	req, err := s.client.UploadRequest(
		http.MethodPut,
		u,
		avatar,
		filename,
		UploadAvatar,
		nil,
		options,
	return do[*Group](s.client,
		withMethod(http.MethodPut),
		withPath("groups/%s", GroupID{gid}),
		withUpload(avatar, filename, UploadAvatar),
		withRequestOpts(options...),
	)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// DeleteGroupOptions represents the available DeleteGroup() options.
+13 −31
Original line number Diff line number Diff line
@@ -120,18 +120,13 @@ type ScheduleExportUploadOptions struct {
// GitLab API docs:
// https://docs.gitlab.com/api/project_import_export/#schedule-an-export
func (s *ProjectImportExportService) ScheduleExport(pid any, opt *ScheduleExportOptions, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/export", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
	_, resp, err := do[none](s.client,
		withMethod(http.MethodPost),
		withPath("projects/%s/export", ProjectID{pid}),
		withAPIOpts(opt),
		withRequestOpts(options...),
	)
	return resp, err
}

// ExportStatus get the status of export.
@@ -187,26 +182,13 @@ type ImportFileOptions struct {
// GitLab API docs:
// https://docs.gitlab.com/api/project_import_export/#import-a-file
func (s *ProjectImportExportService) ImportFromFile(archive io.Reader, opt *ImportFileOptions, options ...RequestOptionFunc) (*ImportStatus, *Response, error) {
	req, err := s.client.UploadRequest(
		http.MethodPost,
		"projects/import",
		archive,
		"archive.tar.gz",
		UploadFile,
		opt,
		options,
	return do[*ImportStatus](s.client,
		withMethod(http.MethodPost),
		withPath("projects/import"),
		withUpload(archive, "archive.tar.gz", UploadFile),
		withAPIOpts(opt),
		withRequestOpts(options...),
	)
	if err != nil {
		return nil, nil, err
	}

	is := new(ImportStatus)
	resp, err := s.client.Do(req, is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}

// ImportStatus get the status of an import.
+5 −26
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package gitlab

import (
	"fmt"
	"io"
	"net/http"
)
@@ -77,32 +76,12 @@ type (
)

func (s *ProjectMarkdownUploadsService) UploadProjectMarkdown(pid any, content io.Reader, filename string, options ...RequestOptionFunc) (*ProjectMarkdownUploadedFile, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/uploads", PathEscape(project))

	req, err := s.client.UploadRequest(
		http.MethodPost,
		u,
		content,
		filename,
		UploadFile,
		nil,
		options,
	return do[*ProjectMarkdownUploadedFile](s.client,
		withMethod(http.MethodPost),
		withPath("projects/%s/uploads", ProjectID{pid}),
		withUpload(content, filename, UploadFile),
		withRequestOpts(options...),
	)
	if err != nil {
		return nil, nil, err
	}

	f := new(ProjectMarkdownUploadedFile)
	resp, err := s.client.Do(req, f)
	if err != nil {
		return nil, resp, err
	}

	return f, resp, nil
}

func (s *ProjectMarkdownUploadsService) ListProjectMarkdownUploads(pid any, options ...RequestOptionFunc) ([]*ProjectMarkdownUpload, *Response, error) {
+29 −108
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@ import (
	"io"
	"net/http"
	"time"

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

type (
@@ -754,33 +752,16 @@ func (s *ProjectsService) CreateProject(opt *CreateProjectOptions, options ...Re
		opt.ContainerExpirationPolicyAttributes.NameRegex = opt.ContainerExpirationPolicyAttributes.NameRegexDelete
	}

	var err error
	var req *retryablehttp.Request

	if opt.Avatar == nil {
		req, err = s.client.NewRequest(http.MethodPost, "projects", opt, options)
	} else {
		req, err = s.client.UploadRequest(
			http.MethodPost,
			"projects",
			opt.Avatar.Image,
			opt.Avatar.Filename,
			UploadAvatar,
			opt,
			options,
		)
	}
	if err != nil {
		return nil, nil, err
	reqOpts := []doOption{
		withMethod(http.MethodPost),
		withPath("projects"),
		withAPIOpts(opt),
		withRequestOpts(options...),
	}

	p := new(Project)
	resp, err := s.client.Do(req, p)
	if err != nil {
		return nil, resp, err
	if opt.Avatar != nil {
		reqOpts = append(reqOpts, withUpload(opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar))
	}

	return p, resp, nil
	return do[*Project](s.client, reqOpts...)
}

// CreateProjectForUserOptions represents the available CreateProjectForUser()
@@ -802,34 +783,16 @@ func (s *ProjectsService) CreateProjectForUser(user int64, opt *CreateProjectFor
		opt.ContainerExpirationPolicyAttributes.NameRegex = opt.ContainerExpirationPolicyAttributes.NameRegexDelete
	}

	var err error
	var req *retryablehttp.Request
	u := fmt.Sprintf("projects/user/%d", user)

	if opt.Avatar == nil {
		req, err = s.client.NewRequest(http.MethodPost, u, opt, options)
	} else {
		req, err = s.client.UploadRequest(
			http.MethodPost,
			u,
			opt.Avatar.Image,
			opt.Avatar.Filename,
			UploadAvatar,
			opt,
			options,
		)
	}
	if err != nil {
		return nil, nil, err
	reqOpts := []doOption{
		withMethod(http.MethodPost),
		withPath("projects/user/%d", user),
		withAPIOpts(opt),
		withRequestOpts(options...),
	}

	p := new(Project)
	resp, err := s.client.Do(req, p)
	if err != nil {
		return nil, resp, err
	if opt.Avatar != nil {
		reqOpts = append(reqOpts, withUpload(opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar))
	}

	return p, resp, nil
	return do[*Project](s.client, reqOpts...)
}

// EditProjectOptions represents the available EditProject() options.
@@ -960,38 +923,16 @@ func (s *ProjectsService) EditProject(pid any, opt *EditProjectOptions, options
		opt.ContainerExpirationPolicyAttributes.NameRegex = opt.ContainerExpirationPolicyAttributes.NameRegexDelete
	}

	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s", PathEscape(project))

	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
	reqOpts := []doOption{
		withMethod(http.MethodPut),
		withPath("projects/%s", ProjectID{pid}),
		withAPIOpts(opt),
		withRequestOpts(options...),
	}

	p := new(Project)
	resp, err := s.client.Do(req, p)
	if err != nil {
		return nil, resp, err
	if opt.Avatar != nil && (opt.Avatar.Filename != "" || opt.Avatar.Image != nil) {
		reqOpts = append(reqOpts, withUpload(opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar))
	}

	return p, resp, nil
	return do[*Project](s.client, reqOpts...)
}

// ForkProjectOptions represents the available ForkProject() options.
@@ -1495,32 +1436,12 @@ func (s *ProjectsService) DeleteProjectForkRelation(pid any, options ...RequestO
// GitLab API docs:
// https://docs.gitlab.com/api/projects/#upload-a-project-avatar
func (s *ProjectsService) UploadAvatar(pid any, avatar io.Reader, filename string, options ...RequestOptionFunc) (*Project, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s", PathEscape(project))

	req, err := s.client.UploadRequest(
		http.MethodPut,
		u,
		avatar,
		filename,
		UploadAvatar,
		nil,
		options,
	return do[*Project](s.client,
		withMethod(http.MethodPut),
		withPath("projects/%s", ProjectID{pid}),
		withUpload(avatar, filename, UploadAvatar),
		withRequestOpts(options...),
	)
	if err != nil {
		return nil, nil, err
	}

	p := new(Project)
	resp, err := s.client.Do(req, p)
	if err != nil {
		return nil, resp, err
	}

	return p, resp, nil
}

// DownloadAvatar downloads an avatar.
Loading