aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/xanzy/go-gitlab/gitlab.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/xanzy/go-gitlab/gitlab.go')
-rw-r--r--vendor/github.com/xanzy/go-gitlab/gitlab.go453
1 files changed, 345 insertions, 108 deletions
diff --git a/vendor/github.com/xanzy/go-gitlab/gitlab.go b/vendor/github.com/xanzy/go-gitlab/gitlab.go
index 80bd1ed..6019d87 100644
--- a/vendor/github.com/xanzy/go-gitlab/gitlab.go
+++ b/vendor/github.com/xanzy/go-gitlab/gitlab.go
@@ -20,6 +20,7 @@ import (
"bytes"
"context"
"encoding/json"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -28,27 +29,30 @@ import (
"sort"
"strconv"
"strings"
+ "time"
"github.com/google/go-querystring/query"
+ "golang.org/x/oauth2"
)
const (
- libraryVersion = "0.2.0"
- defaultBaseURL = "https://gitlab.com/api/v4/"
- userAgent = "go-gitlab/" + libraryVersion
+ defaultBaseURL = "https://gitlab.com/"
+ apiVersionPath = "api/v4/"
+ userAgent = "go-gitlab"
)
-// tokenType represents a token type within GitLab.
+// authType represents an authentication type within GitLab.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/
-type tokenType int
+type authType int
-// List of available token type
+// List of available authentication types.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/
const (
- privateToken tokenType = iota
+ basicAuth authType = iota
oAuthToken
+ privateToken
)
// AccessLevelValue represents a permission level within GitLab.
@@ -60,6 +64,7 @@ type AccessLevelValue int
//
// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
const (
+ NoPermissions AccessLevelValue = 0
GuestPermissions AccessLevelValue = 10
ReporterPermissions AccessLevelValue = 20
DeveloperPermissions AccessLevelValue = 30
@@ -67,6 +72,58 @@ const (
OwnerPermission AccessLevelValue = 50
)
+// BuildStateValue represents a GitLab build state.
+type BuildStateValue string
+
+// These constants represent all valid build states.
+const (
+ Pending BuildStateValue = "pending"
+ Running BuildStateValue = "running"
+ Success BuildStateValue = "success"
+ Failed BuildStateValue = "failed"
+ Canceled BuildStateValue = "canceled"
+ Skipped BuildStateValue = "skipped"
+)
+
+// ISOTime represents an ISO 8601 formatted date
+type ISOTime time.Time
+
+// ISO 8601 date format
+const iso8601 = "2006-01-02"
+
+// MarshalJSON implements the json.Marshaler interface
+func (t ISOTime) MarshalJSON() ([]byte, error) {
+ if y := time.Time(t).Year(); y < 0 || y >= 10000 {
+ // ISO 8901 uses 4 digits for the years
+ return nil, errors.New("ISOTime.MarshalJSON: year outside of range [0,9999]")
+ }
+
+ b := make([]byte, 0, len(iso8601)+2)
+ b = append(b, '"')
+ b = time.Time(t).AppendFormat(b, iso8601)
+ b = append(b, '"')
+
+ return b, nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface
+func (t *ISOTime) UnmarshalJSON(data []byte) error {
+ // Ignore null, like in the main JSON package
+ if string(data) == "null" {
+ return nil
+ }
+
+ isotime, err := time.Parse(`"`+iso8601+`"`, string(data))
+ *t = ISOTime(isotime)
+
+ return err
+}
+
+// String implements the Stringer interface
+func (t ISOTime) String() string {
+ return time.Time(t).Format(iso8601)
+}
+
// NotificationLevelValue represents a notification level.
type NotificationLevelValue int
@@ -92,6 +149,8 @@ func (l *NotificationLevelValue) UnmarshalJSON(data []byte) error {
*l = NotificationLevelValue(raw)
case string:
*l = notificationLevelTypes[raw]
+ case nil:
+ // No action needed.
default:
return fmt.Errorf("json: cannot unmarshal %T into Go value of type %T", raw, *l)
}
@@ -127,6 +186,19 @@ var notificationLevelTypes = map[string]NotificationLevelValue{
"custom": CustomNotificationLevel,
}
+// OrderByValue represent in which order to sort the item
+type OrderByValue string
+
+// These constants represent all valid order by values.
+const (
+ OrderByCreatedAt OrderByValue = "created_at"
+ OrderByID OrderByValue = "id"
+ OrderByIID OrderByValue = "iid"
+ OrderByRef OrderByValue = "ref"
+ OrderByStatus OrderByValue = "status"
+ OrderByUserID OrderByValue = "user_id"
+)
+
// VisibilityValue represents a visibility level within GitLab.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/
@@ -141,56 +213,128 @@ const (
PublicVisibility VisibilityValue = "public"
)
+// MergeMethodValue represents a project merge type within GitLab.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
+type MergeMethodValue string
+
+// List of available merge type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
+const (
+ NoFastForwardMerge MergeMethodValue = "merge"
+ FastForwardMerge MergeMethodValue = "ff"
+ RebaseMerge MergeMethodValue = "rebase_merge"
+)
+
+// EventTypeValue represents actions type for contribution events
+type EventTypeValue string
+
+// List of available action type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types
+const (
+ CreatedEventType EventTypeValue = "created"
+ UpdatedEventType EventTypeValue = "updated"
+ ClosedEventType EventTypeValue = "closed"
+ ReopenedEventType EventTypeValue = "reopened"
+ PushedEventType EventTypeValue = "pushed"
+ CommentedEventType EventTypeValue = "commented"
+ MergedEventType EventTypeValue = "merged"
+ JoinedEventType EventTypeValue = "joined"
+ LeftEventType EventTypeValue = "left"
+ DestroyedEventType EventTypeValue = "destroyed"
+ ExpiredEventType EventTypeValue = "expired"
+)
+
+// EventTargetTypeValue represents actions type value for contribution events
+type EventTargetTypeValue string
+
+// List of available action type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types
+const (
+ IssueEventTargetType EventTargetTypeValue = "issue"
+ MilestoneEventTargetType EventTargetTypeValue = "milestone"
+ MergeRequestEventTargetType EventTargetTypeValue = "merge_request"
+ NoteEventTargetType EventTargetTypeValue = "note"
+ ProjectEventTargetType EventTargetTypeValue = "project"
+ SnippetEventTargetType EventTargetTypeValue = "snippet"
+ UserEventTargetType EventTargetTypeValue = "user"
+)
+
// A Client manages communication with the GitLab API.
type Client struct {
// HTTP client used to communicate with the API.
client *http.Client
// Base URL for API requests. Defaults to the public GitLab API, but can be
- // set to a domain endpoint to use with aself hosted GitLab server. baseURL
+ // set to a domain endpoint to use with a self hosted GitLab server. baseURL
// should always be specified with a trailing slash.
baseURL *url.URL
- // token type used to make authenticated API calls.
- tokenType tokenType
+ // Token type used to make authenticated API calls.
+ authType authType
- // token used to make authenticated API calls.
+ // Username and password used for basix authentication.
+ username, password string
+
+ // Token used to make authenticated API calls.
token string
// User agent used when communicating with the GitLab API.
UserAgent string
// Services used for talking to different parts of the GitLab API.
- Branches *BranchesService
- BuildVariables *BuildVariablesService
- Commits *CommitsService
- DeployKeys *DeployKeysService
- Features *FeaturesService
- Groups *GroupsService
- Issues *IssuesService
- Jobs *JobsService
- Labels *LabelsService
- MergeRequests *MergeRequestsService
- Milestones *MilestonesService
- Namespaces *NamespacesService
- Notes *NotesService
- NotificationSettings *NotificationSettingsService
- Projects *ProjectsService
- ProjectMembers *ProjectMembersService
- ProjectSnippets *ProjectSnippetsService
- Pipelines *PipelinesService
- PipelineTriggers *PipelineTriggersService
- Repositories *RepositoriesService
- RepositoryFiles *RepositoryFilesService
- Services *ServicesService
- Session *SessionService
- Settings *SettingsService
- SystemHooks *SystemHooksService
- Tags *TagsService
- Todos *TodosService
- Users *UsersService
- Version *VersionService
- Wikis *WikisService
+ AwardEmoji *AwardEmojiService
+ Branches *BranchesService
+ BuildVariables *BuildVariablesService
+ BroadcastMessage *BroadcastMessagesService
+ Commits *CommitsService
+ DeployKeys *DeployKeysService
+ Deployments *DeploymentsService
+ Environments *EnvironmentsService
+ Events *EventsService
+ Features *FeaturesService
+ GitIgnoreTemplates *GitIgnoreTemplatesService
+ Groups *GroupsService
+ GroupMembers *GroupMembersService
+ GroupMilestones *GroupMilestonesService
+ Issues *IssuesService
+ IssueLinks *IssueLinksService
+ Jobs *JobsService
+ Boards *IssueBoardsService
+ Labels *LabelsService
+ MergeRequests *MergeRequestsService
+ MergeRequestApprovals *MergeRequestApprovalsService
+ Milestones *MilestonesService
+ Namespaces *NamespacesService
+ Notes *NotesService
+ NotificationSettings *NotificationSettingsService
+ PagesDomains *PagesDomainsService
+ Pipelines *PipelinesService
+ PipelineSchedules *PipelineSchedulesService
+ PipelineTriggers *PipelineTriggersService
+ Projects *ProjectsService
+ ProjectMembers *ProjectMembersService
+ ProjectSnippets *ProjectSnippetsService
+ ProtectedBranches *ProtectedBranchesService
+ Repositories *RepositoriesService
+ RepositoryFiles *RepositoryFilesService
+ Runners *RunnersService
+ Search *SearchService
+ Services *ServicesService
+ Session *SessionService
+ Settings *SettingsService
+ Sidekiq *SidekiqService
+ Snippets *SnippetsService
+ SystemHooks *SystemHooksService
+ Tags *TagsService
+ Todos *TodosService
+ Users *UsersService
+ Validate *ValidateService
+ Version *VersionService
+ Wikis *WikisService
}
// ListOptions specifies the optional parameters to various List methods that
@@ -205,24 +349,64 @@ type ListOptions struct {
// NewClient returns a new GitLab API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
-// authentication, provide a valid private token.
+// authentication, provide a valid private or personal token.
func NewClient(httpClient *http.Client, token string) *Client {
- return newClient(httpClient, privateToken, token)
+ client := newClient(httpClient)
+ client.authType = privateToken
+ client.token = token
+ return client
+}
+
+// NewBasicAuthClient returns a new GitLab API client. If a nil httpClient is
+// provided, http.DefaultClient will be used. To use API methods which require
+// authentication, provide a valid username and password.
+func NewBasicAuthClient(httpClient *http.Client, endpoint, username, password string) (*Client, error) {
+ client := newClient(httpClient)
+ client.authType = basicAuth
+ client.username = username
+ client.password = password
+ client.SetBaseURL(endpoint)
+
+ err := client.requestOAuthToken(context.TODO())
+ if err != nil {
+ return nil, err
+ }
+
+ return client, nil
+}
+
+func (c *Client) requestOAuthToken(ctx context.Context) error {
+ config := &oauth2.Config{
+ Endpoint: oauth2.Endpoint{
+ AuthURL: fmt.Sprintf("%s://%s/oauth/authorize", c.BaseURL().Scheme, c.BaseURL().Host),
+ TokenURL: fmt.Sprintf("%s://%s/oauth/token", c.BaseURL().Scheme, c.BaseURL().Host),
+ },
+ }
+ ctx = context.WithValue(ctx, oauth2.HTTPClient, c.client)
+ t, err := config.PasswordCredentialsToken(ctx, c.username, c.password)
+ if err != nil {
+ return err
+ }
+ c.token = t.AccessToken
+ return nil
}
// NewOAuthClient returns a new GitLab API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// authentication, provide a valid oauth token.
func NewOAuthClient(httpClient *http.Client, token string) *Client {
- return newClient(httpClient, oAuthToken, token)
+ client := newClient(httpClient)
+ client.authType = oAuthToken
+ client.token = token
+ return client
}
-func newClient(httpClient *http.Client, tokenType tokenType, token string) *Client {
+func newClient(httpClient *http.Client) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
- c := &Client{client: httpClient, tokenType: tokenType, token: token, UserAgent: userAgent}
+ c := &Client{client: httpClient, UserAgent: userAgent}
if err := c.SetBaseURL(defaultBaseURL); err != nil {
// Should never happen since defaultBaseURL is our constant.
panic(err)
@@ -232,34 +416,53 @@ func newClient(httpClient *http.Client, tokenType tokenType, token string) *Clie
timeStats := &timeStatsService{client: c}
// Create all the public services.
+ c.AwardEmoji = &AwardEmojiService{client: c}
c.Branches = &BranchesService{client: c}
c.BuildVariables = &BuildVariablesService{client: c}
+ c.BroadcastMessage = &BroadcastMessagesService{client: c}
c.Commits = &CommitsService{client: c}
c.DeployKeys = &DeployKeysService{client: c}
+ c.Deployments = &DeploymentsService{client: c}
+ c.Environments = &EnvironmentsService{client: c}
+ c.Events = &EventsService{client: c}
c.Features = &FeaturesService{client: c}
+ c.GitIgnoreTemplates = &GitIgnoreTemplatesService{client: c}
c.Groups = &GroupsService{client: c}
+ c.GroupMembers = &GroupMembersService{client: c}
+ c.GroupMilestones = &GroupMilestonesService{client: c}
c.Issues = &IssuesService{client: c, timeStats: timeStats}
+ c.IssueLinks = &IssueLinksService{client: c}
c.Jobs = &JobsService{client: c}
+ c.Boards = &IssueBoardsService{client: c}
c.Labels = &LabelsService{client: c}
c.MergeRequests = &MergeRequestsService{client: c, timeStats: timeStats}
+ c.MergeRequestApprovals = &MergeRequestApprovalsService{client: c}
c.Milestones = &MilestonesService{client: c}
c.Namespaces = &NamespacesService{client: c}
c.Notes = &NotesService{client: c}
c.NotificationSettings = &NotificationSettingsService{client: c}
+ c.PagesDomains = &PagesDomainsService{client: c}
+ c.Pipelines = &PipelinesService{client: c}
+ c.PipelineSchedules = &PipelineSchedulesService{client: c}
+ c.PipelineTriggers = &PipelineTriggersService{client: c}
c.Projects = &ProjectsService{client: c}
c.ProjectMembers = &ProjectMembersService{client: c}
c.ProjectSnippets = &ProjectSnippetsService{client: c}
- c.Pipelines = &PipelinesService{client: c}
- c.PipelineTriggers = &PipelineTriggersService{client: c}
+ c.ProtectedBranches = &ProtectedBranchesService{client: c}
c.Repositories = &RepositoriesService{client: c}
c.RepositoryFiles = &RepositoryFilesService{client: c}
+ c.Runners = &RunnersService{client: c}
c.Services = &ServicesService{client: c}
+ c.Search = &SearchService{client: c}
c.Session = &SessionService{client: c}
c.Settings = &SettingsService{client: c}
+ c.Sidekiq = &SidekiqService{client: c}
+ c.Snippets = &SnippetsService{client: c}
c.SystemHooks = &SystemHooksService{client: c}
c.Tags = &TagsService{client: c}
c.Todos = &TodosService{client: c}
c.Users = &UsersService{client: c}
+ c.Validate = &ValidateService{client: c}
c.Version = &VersionService{client: c}
c.Wikis = &WikisService{client: c}
@@ -280,9 +483,19 @@ func (c *Client) SetBaseURL(urlStr string) error {
urlStr += "/"
}
- var err error
- c.baseURL, err = url.Parse(urlStr)
- return err
+ baseURL, err := url.Parse(urlStr)
+ if err != nil {
+ return err
+ }
+
+ if !strings.HasSuffix(baseURL.Path, apiVersionPath) {
+ baseURL.Path += apiVersionPath
+ }
+
+ // Update the base URL of the client.
+ c.baseURL = baseURL
+
+ return nil
}
// NewRequest creates an API request. A relative URL path can be provided in
@@ -314,6 +527,10 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Opti
}
for _, fn := range options {
+ if fn == nil {
+ continue
+ }
+
if err := fn(req); err != nil {
return nil, err
}
@@ -334,11 +551,11 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Opti
req.Header.Set("Accept", "application/json")
- switch c.tokenType {
+ switch c.authType {
+ case basicAuth, oAuthToken:
+ req.Header.Set("Authorization", "Bearer "+c.token)
case privateToken:
req.Header.Set("PRIVATE-TOKEN", c.token)
- case oAuthToken:
- req.Header.Set("Authorization", "Bearer "+c.token)
}
if c.UserAgent != "" {
@@ -358,11 +575,12 @@ type Response struct {
// results. Any or all of these may be set to the zero value for
// responses that are not part of a paginated set, or for which there
// are no additional pages.
-
- NextPage int
- PrevPage int
- FirstPage int
- LastPage int
+ TotalItems int
+ TotalPages int
+ ItemsPerPage int
+ CurrentPage int
+ NextPage int
+ PreviousPage int
}
// newResponse creates a new Response for the provided http.Response.
@@ -372,47 +590,35 @@ func newResponse(r *http.Response) *Response {
return response
}
+const (
+ xTotal = "X-Total"
+ xTotalPages = "X-Total-Pages"
+ xPerPage = "X-Per-Page"
+ xPage = "X-Page"
+ xNextPage = "X-Next-Page"
+ xPrevPage = "X-Prev-Page"
+)
+
// populatePageValues parses the HTTP Link response headers and populates the
// various pagination link values in the Response.
func (r *Response) populatePageValues() {
- if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 {
- for _, link := range strings.Split(links[0], ",") {
- segments := strings.Split(strings.TrimSpace(link), ";")
-
- // link must at least have href and rel
- if len(segments) < 2 {
- continue
- }
-
- // ensure href is properly formatted
- if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") {
- continue
- }
-
- // try to pull out page parameter
- url, err := url.Parse(segments[0][1 : len(segments[0])-1])
- if err != nil {
- continue
- }
- page := url.Query().Get("page")
- if page == "" {
- continue
- }
-
- for _, segment := range segments[1:] {
- switch strings.TrimSpace(segment) {
- case `rel="next"`:
- r.NextPage, _ = strconv.Atoi(page)
- case `rel="prev"`:
- r.PrevPage, _ = strconv.Atoi(page)
- case `rel="first"`:
- r.FirstPage, _ = strconv.Atoi(page)
- case `rel="last"`:
- r.LastPage, _ = strconv.Atoi(page)
- }
-
- }
- }
+ if totalItems := r.Response.Header.Get(xTotal); totalItems != "" {
+ r.TotalItems, _ = strconv.Atoi(totalItems)
+ }
+ if totalPages := r.Response.Header.Get(xTotalPages); totalPages != "" {
+ r.TotalPages, _ = strconv.Atoi(totalPages)
+ }
+ if itemsPerPage := r.Response.Header.Get(xPerPage); itemsPerPage != "" {
+ r.ItemsPerPage, _ = strconv.Atoi(itemsPerPage)
+ }
+ if currentPage := r.Response.Header.Get(xPage); currentPage != "" {
+ r.CurrentPage, _ = strconv.Atoi(currentPage)
+ }
+ if nextPage := r.Response.Header.Get(xNextPage); nextPage != "" {
+ r.NextPage, _ = strconv.Atoi(nextPage)
+ }
+ if previousPage := r.Response.Header.Get(xPrevPage); previousPage != "" {
+ r.PreviousPage, _ = strconv.Atoi(previousPage)
}
}
@@ -428,6 +634,14 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
}
defer resp.Body.Close()
+ if resp.StatusCode == http.StatusUnauthorized && c.authType == basicAuth {
+ err = c.requestOAuthToken(req.Context())
+ if err != nil {
+ return nil, err
+ }
+ return c.Do(req, v)
+ }
+
response := newResponse(resp)
err = CheckResponse(resp)
@@ -466,6 +680,7 @@ func parseID(id interface{}) (string, error) {
// GitLab API docs:
// https://docs.gitlab.com/ce/api/README.html#data-validation-and-error-reporting
type ErrorResponse struct {
+ Body []byte
Response *http.Response
Message string
}
@@ -486,12 +701,14 @@ func CheckResponse(r *http.Response) error {
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && data != nil {
+ errorResponse.Body = data
+
var raw interface{}
if err := json.Unmarshal(data, &raw); err != nil {
errorResponse.Message = "failed to parse unknown error format"
+ } else {
+ errorResponse.Message = parseError(raw)
}
-
- errorResponse.Message = parseError(raw)
}
return errorResponse
@@ -549,16 +766,12 @@ type OptionFunc func(*http.Request) error
// WithSudo takes either a username or user ID and sets the SUDO request header
func WithSudo(uid interface{}) OptionFunc {
return func(req *http.Request) error {
- switch uid := uid.(type) {
- case int:
- req.Header.Set("SUDO", strconv.Itoa(uid))
- return nil
- case string:
- req.Header.Set("SUDO", uid)
- return nil
- default:
- return fmt.Errorf("uid must be either a username or user ID")
+ user, err := parseID(uid)
+ if err != nil {
+ return err
}
+ req.Header.Set("SUDO", user)
+ return nil
}
}
@@ -603,6 +816,14 @@ func AccessLevel(v AccessLevelValue) *AccessLevelValue {
return p
}
+// BuildState is a helper routine that allocates a new BuildStateValue
+// to store v and returns a pointer to it.
+func BuildState(v BuildStateValue) *BuildStateValue {
+ p := new(BuildStateValue)
+ *p = v
+ return p
+}
+
// NotificationLevel is a helper routine that allocates a new NotificationLevelValue
// to store v and returns a pointer to it.
func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
@@ -611,6 +832,14 @@ func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
return p
}
+// OrderBy is a helper routine that allocates a new OrderByValue
+// to store v and returns a pointer to it.
+func OrderBy(v OrderByValue) *OrderByValue {
+ p := new(OrderByValue)
+ *p = v
+ return p
+}
+
// Visibility is a helper routine that allocates a new VisibilityValue
// to store v and returns a pointer to it.
func Visibility(v VisibilityValue) *VisibilityValue {
@@ -618,3 +847,11 @@ func Visibility(v VisibilityValue) *VisibilityValue {
*p = v
return p
}
+
+// MergeMethod is a helper routine that allocates a new MergeMethod
+// to sotre v and returns a pointer to it.
+func MergeMethod(v MergeMethodValue) *MergeMethodValue {
+ p := new(MergeMethodValue)
+ *p = v
+ return p
+}