diff options
Diffstat (limited to 'vendor/github.com/xanzy/go-gitlab/gitlab.go')
-rw-r--r-- | vendor/github.com/xanzy/go-gitlab/gitlab.go | 496 |
1 files changed, 73 insertions, 423 deletions
diff --git a/vendor/github.com/xanzy/go-gitlab/gitlab.go b/vendor/github.com/xanzy/go-gitlab/gitlab.go index 4fdca56351..f8e4ee22d6 100644 --- a/vendor/github.com/xanzy/go-gitlab/gitlab.go +++ b/vendor/github.com/xanzy/go-gitlab/gitlab.go @@ -20,7 +20,6 @@ package gitlab import ( "context" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -63,268 +62,6 @@ const ( privateToken ) -// AccessLevelValue represents a permission level within GitLab. -// -// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html -type AccessLevelValue int - -// List of available access levels -// -// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html -const ( - NoPermissions AccessLevelValue = 0 - GuestPermissions AccessLevelValue = 10 - ReporterPermissions AccessLevelValue = 20 - DeveloperPermissions AccessLevelValue = 30 - MaintainerPermissions AccessLevelValue = 40 - OwnerPermissions AccessLevelValue = 50 - - // These are deprecated and should be removed in a future version - MasterPermissions AccessLevelValue = 40 - OwnerPermission AccessLevelValue = 50 -) - -// BuildStateValue represents a GitLab build state. -type BuildStateValue string - -// These constants represent all valid build states. -const ( - Pending BuildStateValue = "pending" - Created BuildStateValue = "created" - Running BuildStateValue = "running" - Success BuildStateValue = "success" - Failed BuildStateValue = "failed" - Canceled BuildStateValue = "canceled" - Skipped BuildStateValue = "skipped" - Manual BuildStateValue = "manual" -) - -// DeploymentStatusValue represents a Gitlab deployment status. -type DeploymentStatusValue string - -// These constants represent all valid deployment statuses. -const ( - DeploymentStatusCreated DeploymentStatusValue = "created" - DeploymentStatusRunning DeploymentStatusValue = "running" - DeploymentStatusSuccess DeploymentStatusValue = "success" - DeploymentStatusFailed DeploymentStatusValue = "failed" - DeploymentStatusCanceled DeploymentStatusValue = "canceled" -) - -// 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("json: ISOTime 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 -} - -// EncodeValues implements the query.Encoder interface -func (t *ISOTime) EncodeValues(key string, v *url.Values) error { - if t == nil || (time.Time(*t)).IsZero() { - return nil - } - v.Add(key, t.String()) - return nil -} - -// String implements the Stringer interface -func (t ISOTime) String() string { - return time.Time(t).Format(iso8601) -} - -// NotificationLevelValue represents a notification level. -type NotificationLevelValue int - -// String implements the fmt.Stringer interface. -func (l NotificationLevelValue) String() string { - return notificationLevelNames[l] -} - -// MarshalJSON implements the json.Marshaler interface. -func (l NotificationLevelValue) MarshalJSON() ([]byte, error) { - return json.Marshal(l.String()) -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (l *NotificationLevelValue) UnmarshalJSON(data []byte) error { - var raw interface{} - if err := json.Unmarshal(data, &raw); err != nil { - return err - } - - switch raw := raw.(type) { - case float64: - *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) - } - - return nil -} - -// List of valid notification levels. -const ( - DisabledNotificationLevel NotificationLevelValue = iota - ParticipatingNotificationLevel - WatchNotificationLevel - GlobalNotificationLevel - MentionNotificationLevel - CustomNotificationLevel -) - -var notificationLevelNames = [...]string{ - "disabled", - "participating", - "watch", - "global", - "mention", - "custom", -} - -var notificationLevelTypes = map[string]NotificationLevelValue{ - "disabled": DisabledNotificationLevel, - "participating": ParticipatingNotificationLevel, - "watch": WatchNotificationLevel, - "global": GlobalNotificationLevel, - "mention": MentionNotificationLevel, - "custom": CustomNotificationLevel, -} - -// VisibilityValue represents a visibility level within GitLab. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -type VisibilityValue string - -// List of available visibility levels. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -const ( - PrivateVisibility VisibilityValue = "private" - InternalVisibility VisibilityValue = "internal" - PublicVisibility VisibilityValue = "public" -) - -// ProjectCreationLevelValue represents a project creation level within GitLab. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -type ProjectCreationLevelValue string - -// List of available project creation levels. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -const ( - NoOneProjectCreation ProjectCreationLevelValue = "noone" - MaintainerProjectCreation ProjectCreationLevelValue = "maintainer" - DeveloperProjectCreation ProjectCreationLevelValue = "developer" -) - -// SubGroupCreationLevelValue represents a sub group creation level within GitLab. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -type SubGroupCreationLevelValue string - -// List of available sub group creation levels. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -const ( - OwnerSubGroupCreationLevelValue SubGroupCreationLevelValue = "owner" - MaintainerSubGroupCreationLevelValue SubGroupCreationLevelValue = "maintainer" -) - -// VariableTypeValue represents a variable type within GitLab. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -type VariableTypeValue string - -// List of available variable types. -// -// GitLab API docs: https://docs.gitlab.com/ce/api/ -const ( - EnvVariableType VariableTypeValue = "env_var" - FileVariableType VariableTypeValue = "file" -) - -// 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. @@ -338,12 +75,12 @@ type Client struct { // disableRetries is used to disable the default retry logic. disableRetries bool - // configLimiter is used to make sure the limiter is configured exactly + // configureLimiterOnce is used to make sure the limiter is configured exactly // once and block all other calls until the initial (one) call is done. configureLimiterOnce sync.Once // Limiter is used to limit API calls and prevent 429 responses. - limiter *rate.Limiter + limiter RateLimiter // Token type used to make authenticated API calls. authType authType @@ -354,6 +91,9 @@ type Client struct { // Token used to make authenticated API calls. token string + // Protects the token field from concurrent read/write accesses. + tokenLock sync.RWMutex + // User agent used when communicating with the GitLab API. UserAgent string @@ -373,6 +113,7 @@ type Client struct { Deployments *DeploymentsService Discussions *DiscussionsService Environments *EnvironmentsService + EpicIssues *EpicIssuesService Epics *EpicsService Events *EventsService Features *FeaturesService @@ -385,8 +126,11 @@ type Client struct { GroupMilestones *GroupMilestonesService GroupVariables *GroupVariablesService Groups *GroupsService + InstanceCluster *InstanceClustersService + InstanceVariables *InstanceVariablesService IssueLinks *IssueLinksService Issues *IssuesService + IssuesStatistics *IssuesStatisticsService Jobs *JobsService Keys *KeysService Labels *LabelsService @@ -406,6 +150,7 @@ type Client struct { ProjectCluster *ProjectClustersService ProjectImportExport *ProjectImportExportService ProjectMembers *ProjectMembersService + ProjectMirrors *ProjectMirrorService ProjectSnippets *ProjectSnippetsService ProjectVariables *ProjectVariablesService Projects *ProjectsService @@ -441,6 +186,11 @@ type ListOptions struct { PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"` } +// RateLimiter describes the interface that all (custom) rate limiters must implement. +type RateLimiter interface { + Wait(context.Context) error +} + // NewClient returns a new GitLab API client. To use API methods which require // authentication, provide a valid private or personal token. func NewClient(token string, options ...ClientOptionFunc) (*Client, error) { @@ -465,11 +215,6 @@ func NewBasicAuthClient(username, password string, options ...ClientOptionFunc) client.username = username client.password = password - err = client.requestOAuthToken(context.Background()) - if err != nil { - return nil, err - } - return client, nil } @@ -485,22 +230,6 @@ func NewOAuthClient(token string, options ...ClientOptionFunc) (*Client, error) 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 -} - func newClient(options ...ClientOptionFunc) (*Client, error) { c := &Client{UserAgent: userAgent} @@ -547,6 +276,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.Deployments = &DeploymentsService{client: c} c.Discussions = &DiscussionsService{client: c} c.Environments = &EnvironmentsService{client: c} + c.EpicIssues = &EpicIssuesService{client: c} c.Epics = &EpicsService{client: c} c.Events = &EventsService{client: c} c.Features = &FeaturesService{client: c} @@ -559,8 +289,10 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.GroupMilestones = &GroupMilestonesService{client: c} c.GroupVariables = &GroupVariablesService{client: c} c.Groups = &GroupsService{client: c} + c.InstanceCluster = &InstanceClustersService{client: c} c.IssueLinks = &IssueLinksService{client: c} c.Issues = &IssuesService{client: c, timeStats: timeStats} + c.IssuesStatistics = &IssuesStatisticsService{client: c} c.Jobs = &JobsService{client: c} c.Keys = &KeysService{client: c} c.Labels = &LabelsService{client: c} @@ -580,6 +312,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.ProjectCluster = &ProjectClustersService{client: c} c.ProjectImportExport = &ProjectImportExportService{client: c} c.ProjectMembers = &ProjectMembersService{client: c} + c.ProjectMirrors = &ProjectMirrorService{client: c} c.ProjectSnippets = &ProjectSnippetsService{client: c} c.ProjectVariables = &ProjectVariablesService{client: c} c.Projects = &ProjectsService{client: c} @@ -755,13 +488,6 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Requ reqHeaders := make(http.Header) reqHeaders.Set("Accept", "application/json") - switch c.authType { - case basicAuth, oAuthToken: - reqHeaders.Set("Authorization", "Bearer "+c.token) - case privateToken: - reqHeaders.Set("PRIVATE-TOKEN", c.token) - } - if c.UserAgent != "" { reqHeaders.Set("User-Agent", c.UserAgent) } @@ -875,23 +601,47 @@ func (c *Client) Do(req *retryablehttp.Request, v interface{}) (*Response, error c.configureLimiterOnce.Do(func() { c.configureLimiter() }) // Wait will block until the limiter can obtain a new token. - if err := c.limiter.Wait(req.Context()); err != nil { + err := c.limiter.Wait(req.Context()) + if err != nil { return nil, err } + // Set the correct authentication header. If using basic auth, then check + // if we already have a token and if not first authenticate and get one. + var basicAuthToken string + switch c.authType { + case basicAuth: + c.tokenLock.RLock() + basicAuthToken = c.token + c.tokenLock.RUnlock() + if basicAuthToken == "" { + // If we don't have a token yet, we first need to request one. + basicAuthToken, err = c.requestOAuthToken(req.Context(), basicAuthToken) + if err != nil { + return nil, err + } + } + req.Header.Set("Authorization", "Bearer "+basicAuthToken) + case oAuthToken: + req.Header.Set("Authorization", "Bearer "+c.token) + case privateToken: + req.Header.Set("PRIVATE-TOKEN", c.token) + } + resp, err := c.client.Do(req) if err != nil { return nil, err } - defer resp.Body.Close() if resp.StatusCode == http.StatusUnauthorized && c.authType == basicAuth { - err = c.requestOAuthToken(req.Context()) - if err != nil { + resp.Body.Close() + // The token most likely expired, so we need to request a new one and try again. + if _, err := c.requestOAuthToken(req.Context(), basicAuthToken); err != nil { return nil, err } return c.Do(req, v) } + defer resp.Body.Close() response := newResponse(resp) @@ -913,6 +663,32 @@ func (c *Client) Do(req *retryablehttp.Request, v interface{}) (*Response, error return response, err } +func (c *Client) requestOAuthToken(ctx context.Context, token string) (string, error) { + c.tokenLock.Lock() + defer c.tokenLock.Unlock() + + // Return early if the token was updated while waiting for the lock. + if c.token != token { + return c.token, nil + } + + config := &oauth2.Config{ + Endpoint: oauth2.Endpoint{ + AuthURL: strings.TrimSuffix(c.baseURL.String(), apiVersionPath) + "oauth/authorize", + TokenURL: strings.TrimSuffix(c.baseURL.String(), apiVersionPath) + "oauth/token", + }, + } + + ctx = context.WithValue(ctx, oauth2.HTTPClient, c.client.HTTPClient) + t, err := config.PasswordCredentialsToken(ctx, c.username, c.password) + if err != nil { + return "", err + } + c.token = t.AccessToken + + return c.token, nil +} + // Helper function to accept and format both the project ID or name as project // identifier for all API calls. func parseID(id interface{}) (string, error) { @@ -1012,129 +788,3 @@ func parseError(raw interface{}) string { return fmt.Sprintf("failed to parse unexpected error type: %T", raw) } } - -// Bool is a helper routine that allocates a new bool value -// to store v and returns a pointer to it. -func Bool(v bool) *bool { - p := new(bool) - *p = v - return p -} - -// Int is a helper routine that allocates a new int32 value -// to store v and returns a pointer to it, but unlike Int32 -// its argument value is an int. -func Int(v int) *int { - p := new(int) - *p = v - return p -} - -// String is a helper routine that allocates a new string value -// to store v and returns a pointer to it. -func String(v string) *string { - p := new(string) - *p = v - return p -} - -// Time is a helper routine that allocates a new time.Time value -// to store v and returns a pointer to it. -func Time(v time.Time) *time.Time { - p := new(time.Time) - *p = v - return p -} - -// AccessLevel is a helper routine that allocates a new AccessLevelValue -// to store v and returns a pointer to it. -func AccessLevel(v AccessLevelValue) *AccessLevelValue { - p := new(AccessLevelValue) - *p = v - 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 -} - -// DeploymentStatus is a helper routine that allocates a new -// DeploymentStatusValue to store v and returns a pointer to it. -func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue { - p := new(DeploymentStatusValue) - *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 { - p := new(NotificationLevelValue) - *p = v - return p -} - -// VariableType is a helper routine that allocates a new VariableTypeValue -// to store v and returns a pointer to it. -func VariableType(v VariableTypeValue) *VariableTypeValue { - p := new(VariableTypeValue) - *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 { - p := new(VisibilityValue) - *p = v - return p -} - -// ProjectCreationLevel is a helper routine that allocates a new ProjectCreationLevelValue -// to store v and returns a pointer to it. -func ProjectCreationLevel(v ProjectCreationLevelValue) *ProjectCreationLevelValue { - p := new(ProjectCreationLevelValue) - *p = v - return p -} - -// SubGroupCreationLevel is a helper routine that allocates a new SubGroupCreationLevelValue -// to store v and returns a pointer to it. -func SubGroupCreationLevel(v SubGroupCreationLevelValue) *SubGroupCreationLevelValue { - p := new(SubGroupCreationLevelValue) - *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 -} - -// BoolValue is a boolean value with advanced json unmarshaling features. -type BoolValue bool - -// UnmarshalJSON allows 1 and 0 to be considered as boolean values -// Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/50122 -func (t *BoolValue) UnmarshalJSON(b []byte) error { - switch string(b) { - case `"1"`: - *t = true - return nil - case `"0"`: - *t = false - return nil - default: - var v bool - err := json.Unmarshal(b, &v) - *t = BoolValue(v) - return err - } -} |