From 17d06d3003a796c76c7c5d8bfb0cab0aeb1bbf8f Mon Sep 17 00:00:00 2001 From: Kevin Lyda Date: Sun, 22 Jan 2017 23:25:20 +0000 Subject: Create a gitlab auth source. Defaults to public gitlab.com, but easily redirected to self-hosted installation. --- vendor/github.com/xanzy/go-gitlab/gitlab.go | 492 ++++++++++++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 vendor/github.com/xanzy/go-gitlab/gitlab.go (limited to 'vendor/github.com/xanzy/go-gitlab/gitlab.go') diff --git a/vendor/github.com/xanzy/go-gitlab/gitlab.go b/vendor/github.com/xanzy/go-gitlab/gitlab.go new file mode 100644 index 0000000..eb0ca76 --- /dev/null +++ b/vendor/github.com/xanzy/go-gitlab/gitlab.go @@ -0,0 +1,492 @@ +// +// Copyright 2015, Sander van Harmelen +// +// 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 ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" + + "github.com/google/go-querystring/query" +) + +const ( + libraryVersion = "0.1.1" + defaultBaseURL = "https://gitlab.com/api/v3/" + userAgent = "go-gitlab/" + libraryVersion +) + +// tokenType represents a token type within GitLab. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +type tokenType int + +// List of available token type +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +const ( + privateToken tokenType = iota + oAuthToken +) + +// 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 ( + GuestPermissions AccessLevelValue = 10 + ReporterPermissions AccessLevelValue = 20 + DeveloperPermissions AccessLevelValue = 30 + MasterPermissions AccessLevelValue = 40 + OwnerPermission AccessLevelValue = 50 +) + +// NotificationLevelValue represents a notification level within Gitlab. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +type NotificationLevelValue int + +// List of available notification levels +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +const ( + DisabledNotifications NotificationLevelValue = iota + ParticipatingNotifications + WatchNotifications + GlobalNotifications + MentionNotifications +) + +// VisibilityLevelValue represents a visibility level within GitLab. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +type VisibilityLevelValue int + +// List of available visibility levels +// +// GitLab API docs: https://docs.gitlab.com/ce/api/ +const ( + PrivateVisibility VisibilityLevelValue = 0 + InternalVisibility VisibilityLevelValue = 10 + PublicVisibility VisibilityLevelValue = 20 +) + +// 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 + // should always be specified with a trailing slash. + baseURL *url.URL + + // token type used to make authenticated API calls. + tokenType tokenType + + // 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 + Builds *BuildsService + Commits *CommitsService + DeployKeys *DeployKeysService + Groups *GroupsService + Issues *IssuesService + Labels *LabelsService + MergeRequests *MergeRequestsService + Milestones *MilestonesService + Namespaces *NamespacesService + Notes *NotesService + Projects *ProjectsService + ProjectSnippets *ProjectSnippetsService + Pipelines *PipelinesService + Repositories *RepositoriesService + RepositoryFiles *RepositoryFilesService + Services *ServicesService + Session *SessionService + Settings *SettingsService + SystemHooks *SystemHooksService + Tags *TagsService + Users *UsersService +} + +// ListOptions specifies the optional parameters to various List methods that +// support pagination. +type ListOptions struct { + // For paginated result sets, page of results to retrieve. + Page int `url:"page,omitempty" json:"page,omitempty"` + + // For paginated result sets, the number of results to include per page. + PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"` +} + +// 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. +func NewClient(httpClient *http.Client, token string) *Client { + return newClient(httpClient, privateToken, token) +} + +// 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) +} + +func newClient(httpClient *http.Client, tokenType tokenType, token string) *Client { + if httpClient == nil { + httpClient = http.DefaultClient + } + + c := &Client{client: httpClient, tokenType: tokenType, token: token, UserAgent: userAgent} + if err := c.SetBaseURL(defaultBaseURL); err != nil { + // should never happen since defaultBaseURL is our constant + panic(err) + } + + c.Branches = &BranchesService{client: c} + c.BuildVariables = &BuildVariablesService{client: c} + c.Builds = &BuildsService{client: c} + c.Commits = &CommitsService{client: c} + c.DeployKeys = &DeployKeysService{client: c} + c.Groups = &GroupsService{client: c} + c.Issues = &IssuesService{client: c} + c.Labels = &LabelsService{client: c} + c.MergeRequests = &MergeRequestsService{client: c} + c.Milestones = &MilestonesService{client: c} + c.Notes = &NotesService{client: c} + c.Namespaces = &NamespacesService{client: c} + c.Projects = &ProjectsService{client: c} + c.ProjectSnippets = &ProjectSnippetsService{client: c} + c.Pipelines = &PipelinesService{client: c} + c.Repositories = &RepositoriesService{client: c} + c.RepositoryFiles = &RepositoryFilesService{client: c} + c.Services = &ServicesService{client: c} + c.Session = &SessionService{client: c} + c.Settings = &SettingsService{client: c} + c.SystemHooks = &SystemHooksService{client: c} + c.Tags = &TagsService{client: c} + c.Users = &UsersService{client: c} + + return c +} + +// BaseURL return a copy of the baseURL. +func (c *Client) BaseURL() *url.URL { + u := *c.baseURL + return &u +} + +// SetBaseURL sets the base URL for API requests to a custom endpoint. urlStr +// should always be specified with a trailing slash. +func (c *Client) SetBaseURL(urlStr string) error { + // Make sure the given URL end with a slash + if !strings.HasSuffix(urlStr, "/") { + urlStr += "/" + } + + var err error + c.baseURL, err = url.Parse(urlStr) + return err +} + +// NewRequest creates an API request. A relative URL path can be provided in +// urlStr, in which case it is resolved relative to the base URL of the Client. +// Relative URL paths should always be specified without a preceding slash. If +// specified, the value pointed to by body is JSON encoded and included as the +// request body. +func (c *Client) NewRequest(method, path string, opt interface{}) (*http.Request, error) { + u := *c.baseURL + // Set the encoded opaque data + u.Opaque = c.baseURL.Path + path + + if opt != nil { + q, err := query.Values(opt) + if err != nil { + return nil, err + } + u.RawQuery = q.Encode() + } + + req := &http.Request{ + Method: method, + URL: &u, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Host: u.Host, + } + + if method == "POST" || method == "PUT" { + bodyBytes, err := json.Marshal(opt) + if err != nil { + return nil, err + } + bodyReader := bytes.NewReader(bodyBytes) + + u.RawQuery = "" + req.Body = ioutil.NopCloser(bodyReader) + req.ContentLength = int64(bodyReader.Len()) + req.Header.Set("Content-Type", "application/json") + } + + req.Header.Set("Accept", "application/json") + + switch c.tokenType { + case privateToken: + req.Header.Set("PRIVATE-TOKEN", c.token) + case oAuthToken: + req.Header.Set("Authorization", "Bearer "+c.token) + } + + if c.UserAgent != "" { + req.Header.Set("User-Agent", c.UserAgent) + } + + return req, nil +} + +// Response is a GitLab API response. This wraps the standard http.Response +// returned from GitLab and provides convenient access to things like +// pagination links. +type Response struct { + *http.Response + + // These fields provide the page values for paginating through a set of + // 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 +} + +// newResponse creats a new Response for the provided http.Response. +func newResponse(r *http.Response) *Response { + response := &Response{Response: r} + response.populatePageValues() + return response +} + +// populatePageValues parses the HTTP Link response headers and populates the +// various pagination link values in the Reponse. +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) + } + + } + } + } +} + +// Do sends an API request and returns the API response. The API response is +// JSON decoded and stored in the value pointed to by v, or returned as an +// error if an API error has occurred. If v implements the io.Writer +// interface, the raw response body will be written to v, without attempting to +// first decode it. +func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + response := newResponse(resp) + + err = CheckResponse(resp) + if err != nil { + // even though there was an error, we still return the response + // in case the caller wants to inspect it further + return response, err + } + + if v != nil { + if w, ok := v.(io.Writer); ok { + _, err = io.Copy(w, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(v) + } + } + return response, err +} + +// 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) { + switch v := id.(type) { + case int: + return strconv.Itoa(v), nil + case string: + return v, nil + default: + return "", fmt.Errorf("invalid ID type %#v, the ID must be an int or a string", id) + } +} + +// An ErrorResponse reports one or more errors caused by an API request. +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/README.html#data-validation-and-error-reporting +type ErrorResponse struct { + Response *http.Response // HTTP response that caused this error + Message string `json:"message"` // error message + Errors []Error `json:"errors"` // more detail on individual errors +} + +func (r *ErrorResponse) Error() string { + path, _ := url.QueryUnescape(r.Response.Request.URL.Opaque) + ru := fmt.Sprintf("%s://%s%s", r.Response.Request.URL.Scheme, r.Response.Request.URL.Host, path) + + return fmt.Sprintf("%v %s: %d %v %+v", + r.Response.Request.Method, ru, r.Response.StatusCode, r.Message, r.Errors) +} + +// An Error reports more details on an individual error in an ErrorResponse. +// These are the possible validation error codes: +// +// missing: +// resource does not exist +// missing_field: +// a required field on a resource has not been set +// invalid: +// the formatting of a field is invalid +// already_exists: +// another resource has the same valid as this field +// +// GitLab API docs: +// https://docs.gitlab.com/ce/api/README.html#data-validation-and-error-reporting +type Error struct { + Resource string `json:"resource"` // resource on which the error occurred + Field string `json:"field"` // field on which the error occurred + Code string `json:"code"` // validation error code +} + +func (e *Error) Error() string { + return fmt.Sprintf("%v error caused by %v field on %v resource", + e.Code, e.Field, e.Resource) +} + +// CheckResponse checks the API response for errors, and returns them if +// present. A response is considered an error if it has a status code outside +// the 200 range. API error responses are expected to have either no response +// body, or a JSON response body that maps to ErrorResponse. Any other +// response body will be silently ignored. +func CheckResponse(r *http.Response) error { + if c := r.StatusCode; 200 <= c && c <= 299 { + return nil + } + errorResponse := &ErrorResponse{Response: r} + data, err := ioutil.ReadAll(r.Body) + if err == nil && data != nil { + json.Unmarshal(data, errorResponse) + } + return errorResponse +} + +// 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 +} + +// 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 +} + +// VisibilityLevel is a helper routine that allocates a new VisibilityLevelValue +// to store v and returns a pointer to it. +func VisibilityLevel(v VisibilityLevelValue) *VisibilityLevelValue { + p := new(VisibilityLevelValue) + *p = v + return p +} -- cgit v1.2.3