projects.go

   1//
   2// Copyright 2017, Sander van Harmelen
   3//
   4// Licensed under the Apache License, Version 2.0 (the "License");
   5// you may not use this file except in compliance with the License.
   6// You may obtain a copy of the License at
   7//
   8//     http://www.apache.org/licenses/LICENSE-2.0
   9//
  10// Unless required by applicable law or agreed to in writing, software
  11// distributed under the License is distributed on an "AS IS" BASIS,
  12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13// See the License for the specific language governing permissions and
  14// limitations under the License.
  15//
  16
  17package gitlab
  18
  19import (
  20	"bytes"
  21	"fmt"
  22	"io"
  23	"io/ioutil"
  24	"mime/multipart"
  25	"os"
  26	"time"
  27)
  28
  29// ProjectsService handles communication with the repositories related methods
  30// of the GitLab API.
  31//
  32// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
  33type ProjectsService struct {
  34	client *Client
  35}
  36
  37// Project represents a GitLab project.
  38//
  39// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
  40type Project struct {
  41	ID                                        int               `json:"id"`
  42	Description                               string            `json:"description"`
  43	DefaultBranch                             string            `json:"default_branch"`
  44	Public                                    bool              `json:"public"`
  45	Visibility                                VisibilityValue   `json:"visibility"`
  46	SSHURLToRepo                              string            `json:"ssh_url_to_repo"`
  47	HTTPURLToRepo                             string            `json:"http_url_to_repo"`
  48	WebURL                                    string            `json:"web_url"`
  49	ReadmeURL                                 string            `json:"readme_url"`
  50	TagList                                   []string          `json:"tag_list"`
  51	Owner                                     *User             `json:"owner"`
  52	Name                                      string            `json:"name"`
  53	NameWithNamespace                         string            `json:"name_with_namespace"`
  54	Path                                      string            `json:"path"`
  55	PathWithNamespace                         string            `json:"path_with_namespace"`
  56	IssuesEnabled                             bool              `json:"issues_enabled"`
  57	OpenIssuesCount                           int               `json:"open_issues_count"`
  58	MergeRequestsEnabled                      bool              `json:"merge_requests_enabled"`
  59	ApprovalsBeforeMerge                      int               `json:"approvals_before_merge"`
  60	JobsEnabled                               bool              `json:"jobs_enabled"`
  61	WikiEnabled                               bool              `json:"wiki_enabled"`
  62	SnippetsEnabled                           bool              `json:"snippets_enabled"`
  63	ResolveOutdatedDiffDiscussions            bool              `json:"resolve_outdated_diff_discussions"`
  64	ContainerRegistryEnabled                  bool              `json:"container_registry_enabled"`
  65	CreatedAt                                 *time.Time        `json:"created_at,omitempty"`
  66	LastActivityAt                            *time.Time        `json:"last_activity_at,omitempty"`
  67	CreatorID                                 int               `json:"creator_id"`
  68	Namespace                                 *ProjectNamespace `json:"namespace"`
  69	ImportStatus                              string            `json:"import_status"`
  70	ImportError                               string            `json:"import_error"`
  71	Permissions                               *Permissions      `json:"permissions"`
  72	Archived                                  bool              `json:"archived"`
  73	AvatarURL                                 string            `json:"avatar_url"`
  74	SharedRunnersEnabled                      bool              `json:"shared_runners_enabled"`
  75	ForksCount                                int               `json:"forks_count"`
  76	StarCount                                 int               `json:"star_count"`
  77	RunnersToken                              string            `json:"runners_token"`
  78	PublicBuilds                              bool              `json:"public_builds"`
  79	OnlyAllowMergeIfPipelineSucceeds          bool              `json:"only_allow_merge_if_pipeline_succeeds"`
  80	OnlyAllowMergeIfAllDiscussionsAreResolved bool              `json:"only_allow_merge_if_all_discussions_are_resolved"`
  81	LFSEnabled                                bool              `json:"lfs_enabled"`
  82	RequestAccessEnabled                      bool              `json:"request_access_enabled"`
  83	MergeMethod                               MergeMethodValue  `json:"merge_method"`
  84	ForkedFromProject                         *ForkParent       `json:"forked_from_project"`
  85	Mirror                                    bool              `json:"mirror"`
  86	MirrorUserID                              int               `json:"mirror_user_id"`
  87	MirrorTriggerBuilds                       bool              `json:"mirror_trigger_builds"`
  88	OnlyMirrorProtectedBranches               bool              `json:"only_mirror_protected_branches"`
  89	MirrorOverwritesDivergedBranches          bool              `json:"mirror_overwrites_diverged_branches"`
  90	SharedWithGroups                          []struct {
  91		GroupID          int    `json:"group_id"`
  92		GroupName        string `json:"group_name"`
  93		GroupAccessLevel int    `json:"group_access_level"`
  94	} `json:"shared_with_groups"`
  95	Statistics       *ProjectStatistics `json:"statistics"`
  96	Links            *Links             `json:"_links,omitempty"`
  97	CIConfigPath     *string            `json:"ci_config_path"`
  98	CustomAttributes []*CustomAttribute `json:"custom_attributes"`
  99}
 100
 101// Repository represents a repository.
 102type Repository struct {
 103	Name              string          `json:"name"`
 104	Description       string          `json:"description"`
 105	WebURL            string          `json:"web_url"`
 106	AvatarURL         string          `json:"avatar_url"`
 107	GitSSHURL         string          `json:"git_ssh_url"`
 108	GitHTTPURL        string          `json:"git_http_url"`
 109	Namespace         string          `json:"namespace"`
 110	Visibility        VisibilityValue `json:"visibility"`
 111	PathWithNamespace string          `json:"path_with_namespace"`
 112	DefaultBranch     string          `json:"default_branch"`
 113	Homepage          string          `json:"homepage"`
 114	URL               string          `json:"url"`
 115	SSHURL            string          `json:"ssh_url"`
 116	HTTPURL           string          `json:"http_url"`
 117}
 118
 119// ProjectNamespace represents a project namespace.
 120type ProjectNamespace struct {
 121	ID       int    `json:"id"`
 122	Name     string `json:"name"`
 123	Path     string `json:"path"`
 124	Kind     string `json:"kind"`
 125	FullPath string `json:"full_path"`
 126}
 127
 128// StorageStatistics represents a statistics record for a group or project.
 129type StorageStatistics struct {
 130	StorageSize      int64 `json:"storage_size"`
 131	RepositorySize   int64 `json:"repository_size"`
 132	LfsObjectsSize   int64 `json:"lfs_objects_size"`
 133	JobArtifactsSize int64 `json:"job_artifacts_size"`
 134}
 135
 136// ProjectStatistics represents a statistics record for a project.
 137type ProjectStatistics struct {
 138	StorageStatistics
 139	CommitCount int `json:"commit_count"`
 140}
 141
 142// Permissions represents permissions.
 143type Permissions struct {
 144	ProjectAccess *ProjectAccess `json:"project_access"`
 145	GroupAccess   *GroupAccess   `json:"group_access"`
 146}
 147
 148// ProjectAccess represents project access.
 149type ProjectAccess struct {
 150	AccessLevel       AccessLevelValue       `json:"access_level"`
 151	NotificationLevel NotificationLevelValue `json:"notification_level"`
 152}
 153
 154// GroupAccess represents group access.
 155type GroupAccess struct {
 156	AccessLevel       AccessLevelValue       `json:"access_level"`
 157	NotificationLevel NotificationLevelValue `json:"notification_level"`
 158}
 159
 160// ForkParent represents the parent project when this is a fork.
 161type ForkParent struct {
 162	HTTPURLToRepo     string `json:"http_url_to_repo"`
 163	ID                int    `json:"id"`
 164	Name              string `json:"name"`
 165	NameWithNamespace string `json:"name_with_namespace"`
 166	Path              string `json:"path"`
 167	PathWithNamespace string `json:"path_with_namespace"`
 168	WebURL            string `json:"web_url"`
 169}
 170
 171// Links represents a project web links for self, issues, merge_requests,
 172// repo_branches, labels, events, members.
 173type Links struct {
 174	Self          string `json:"self"`
 175	Issues        string `json:"issues"`
 176	MergeRequests string `json:"merge_requests"`
 177	RepoBranches  string `json:"repo_branches"`
 178	Labels        string `json:"labels"`
 179	Events        string `json:"events"`
 180	Members       string `json:"members"`
 181}
 182
 183func (s Project) String() string {
 184	return Stringify(s)
 185}
 186
 187// ListProjectsOptions represents the available ListProjects() options.
 188//
 189// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects
 190type ListProjectsOptions struct {
 191	ListOptions
 192	Archived                 *bool             `url:"archived,omitempty" json:"archived,omitempty"`
 193	OrderBy                  *string           `url:"order_by,omitempty" json:"order_by,omitempty"`
 194	Sort                     *string           `url:"sort,omitempty" json:"sort,omitempty"`
 195	Search                   *string           `url:"search,omitempty" json:"search,omitempty"`
 196	Simple                   *bool             `url:"simple,omitempty" json:"simple,omitempty"`
 197	Owned                    *bool             `url:"owned,omitempty" json:"owned,omitempty"`
 198	Membership               *bool             `url:"membership,omitempty" json:"membership,omitempty"`
 199	Starred                  *bool             `url:"starred,omitempty" json:"starred,omitempty"`
 200	Statistics               *bool             `url:"statistics,omitempty" json:"statistics,omitempty"`
 201	Visibility               *VisibilityValue  `url:"visibility,omitempty" json:"visibility,omitempty"`
 202	WithIssuesEnabled        *bool             `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
 203	WithMergeRequestsEnabled *bool             `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
 204	MinAccessLevel           *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
 205	WithCustomAttributes     *bool             `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
 206}
 207
 208// ListProjects gets a list of projects accessible by the authenticated user.
 209//
 210// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects
 211func (s *ProjectsService) ListProjects(opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
 212	req, err := s.client.NewRequest("GET", "projects", opt, options)
 213	if err != nil {
 214		return nil, nil, err
 215	}
 216
 217	var p []*Project
 218	resp, err := s.client.Do(req, &p)
 219	if err != nil {
 220		return nil, resp, err
 221	}
 222
 223	return p, resp, err
 224}
 225
 226// ListUserProjects gets a list of projects for the given user.
 227//
 228// GitLab API docs:
 229// https://docs.gitlab.com/ce/api/projects.html#list-user-projects
 230func (s *ProjectsService) ListUserProjects(uid interface{}, opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
 231	user, err := parseID(uid)
 232	if err != nil {
 233		return nil, nil, err
 234	}
 235	u := fmt.Sprintf("users/%s/projects", user)
 236
 237	req, err := s.client.NewRequest("GET", u, opt, options)
 238	if err != nil {
 239		return nil, nil, err
 240	}
 241
 242	var p []*Project
 243	resp, err := s.client.Do(req, &p)
 244	if err != nil {
 245		return nil, resp, err
 246	}
 247
 248	return p, resp, err
 249}
 250
 251// ProjectUser represents a GitLab project user.
 252type ProjectUser struct {
 253	ID        int    `json:"id"`
 254	Name      string `json:"name"`
 255	Username  string `json:"username"`
 256	State     string `json:"state"`
 257	AvatarURL string `json:"avatar_url"`
 258	WebURL    string `json:"web_url"`
 259}
 260
 261// ListProjectUserOptions represents the available ListProjectsUsers() options.
 262//
 263// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#get-project-users
 264type ListProjectUserOptions struct {
 265	ListOptions
 266	Search *string `url:"search,omitempty" json:"search,omitempty"`
 267}
 268
 269// ListProjectsUsers gets a list of users for the given project.
 270//
 271// GitLab API docs:
 272// https://docs.gitlab.com/ce/api/projects.html#get-project-users
 273func (s *ProjectsService) ListProjectsUsers(pid interface{}, opt *ListProjectUserOptions, options ...OptionFunc) ([]*ProjectUser, *Response, error) {
 274	project, err := parseID(pid)
 275	if err != nil {
 276		return nil, nil, err
 277	}
 278	u := fmt.Sprintf("projects/%s/users", pathEscape(project))
 279
 280	req, err := s.client.NewRequest("GET", u, opt, options)
 281	if err != nil {
 282		return nil, nil, err
 283	}
 284
 285	var p []*ProjectUser
 286	resp, err := s.client.Do(req, &p)
 287	if err != nil {
 288		return nil, resp, err
 289	}
 290
 291	return p, resp, err
 292}
 293
 294// ProjectLanguages is a map of strings because the response is arbitrary
 295//
 296// Gitlab API docs: https://docs.gitlab.com/ce/api/projects.html#languages
 297type ProjectLanguages map[string]float32
 298
 299// GetProjectLanguages gets a list of languages used by the project
 300//
 301// GitLab API docs:  https://docs.gitlab.com/ce/api/projects.html#languages
 302func (s *ProjectsService) GetProjectLanguages(pid interface{}, options ...OptionFunc) (*ProjectLanguages, *Response, error) {
 303	project, err := parseID(pid)
 304	if err != nil {
 305		return nil, nil, err
 306	}
 307	u := fmt.Sprintf("projects/%s/languages", pathEscape(project))
 308
 309	req, err := s.client.NewRequest("GET", u, nil, options)
 310	if err != nil {
 311		return nil, nil, err
 312	}
 313
 314	p := new(ProjectLanguages)
 315	resp, err := s.client.Do(req, p)
 316	if err != nil {
 317		return nil, resp, err
 318	}
 319
 320	return p, resp, err
 321}
 322
 323// GetProjectOptions represents the available GetProject() options.
 324//
 325// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#get-single-project
 326type GetProjectOptions struct {
 327	Statistics           *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
 328	License              *bool `url:"license,omitempty" json:"license,omitempty"`
 329	WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
 330}
 331
 332// GetProject gets a specific project, identified by project ID or
 333// NAMESPACE/PROJECT_NAME, which is owned by the authenticated user.
 334//
 335// GitLab API docs:
 336// https://docs.gitlab.com/ce/api/projects.html#get-single-project
 337func (s *ProjectsService) GetProject(pid interface{}, opt *GetProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
 338	project, err := parseID(pid)
 339	if err != nil {
 340		return nil, nil, err
 341	}
 342	u := fmt.Sprintf("projects/%s", pathEscape(project))
 343
 344	req, err := s.client.NewRequest("GET", u, opt, options)
 345	if err != nil {
 346		return nil, nil, err
 347	}
 348
 349	p := new(Project)
 350	resp, err := s.client.Do(req, p)
 351	if err != nil {
 352		return nil, resp, err
 353	}
 354
 355	return p, resp, err
 356}
 357
 358// ProjectEvent represents a GitLab project event.
 359//
 360// GitLab API docs:
 361// https://docs.gitlab.com/ce/api/projects.html#get-project-events
 362type ProjectEvent struct {
 363	Title          interface{} `json:"title"`
 364	ProjectID      int         `json:"project_id"`
 365	ActionName     string      `json:"action_name"`
 366	TargetID       interface{} `json:"target_id"`
 367	TargetType     interface{} `json:"target_type"`
 368	AuthorID       int         `json:"author_id"`
 369	AuthorUsername string      `json:"author_username"`
 370	Data           struct {
 371		Before            string      `json:"before"`
 372		After             string      `json:"after"`
 373		Ref               string      `json:"ref"`
 374		UserID            int         `json:"user_id"`
 375		UserName          string      `json:"user_name"`
 376		Repository        *Repository `json:"repository"`
 377		Commits           []*Commit   `json:"commits"`
 378		TotalCommitsCount int         `json:"total_commits_count"`
 379	} `json:"data"`
 380	TargetTitle interface{} `json:"target_title"`
 381}
 382
 383func (s ProjectEvent) String() string {
 384	return Stringify(s)
 385}
 386
 387// GetProjectEventsOptions represents the available GetProjectEvents() options.
 388//
 389// GitLab API docs:
 390// https://docs.gitlab.com/ce/api/projects.html#get-project-events
 391type GetProjectEventsOptions ListOptions
 392
 393// GetProjectEvents gets the events for the specified project. Sorted from
 394// newest to latest.
 395//
 396// GitLab API docs:
 397// https://docs.gitlab.com/ce/api/projects.html#get-project-events
 398func (s *ProjectsService) GetProjectEvents(pid interface{}, opt *GetProjectEventsOptions, options ...OptionFunc) ([]*ProjectEvent, *Response, error) {
 399	project, err := parseID(pid)
 400	if err != nil {
 401		return nil, nil, err
 402	}
 403	u := fmt.Sprintf("projects/%s/events", pathEscape(project))
 404
 405	req, err := s.client.NewRequest("GET", u, opt, options)
 406	if err != nil {
 407		return nil, nil, err
 408	}
 409
 410	var p []*ProjectEvent
 411	resp, err := s.client.Do(req, &p)
 412	if err != nil {
 413		return nil, resp, err
 414	}
 415
 416	return p, resp, err
 417}
 418
 419// CreateProjectOptions represents the available CreateProject() options.
 420//
 421// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project
 422type CreateProjectOptions struct {
 423	Name                                      *string           `url:"name,omitempty" json:"name,omitempty"`
 424	Path                                      *string           `url:"path,omitempty" json:"path,omitempty"`
 425	DefaultBranch                             *string           `url:"default_branch,omitempty" json:"default_branch,omitempty"`
 426	NamespaceID                               *int              `url:"namespace_id,omitempty" json:"namespace_id,omitempty"`
 427	Description                               *string           `url:"description,omitempty" json:"description,omitempty"`
 428	IssuesEnabled                             *bool             `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
 429	MergeRequestsEnabled                      *bool             `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
 430	JobsEnabled                               *bool             `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
 431	WikiEnabled                               *bool             `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
 432	SnippetsEnabled                           *bool             `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
 433	ResolveOutdatedDiffDiscussions            *bool             `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
 434	ContainerRegistryEnabled                  *bool             `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
 435	SharedRunnersEnabled                      *bool             `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
 436	Visibility                                *VisibilityValue  `url:"visibility,omitempty" json:"visibility,omitempty"`
 437	ImportURL                                 *string           `url:"import_url,omitempty" json:"import_url,omitempty"`
 438	PublicBuilds                              *bool             `url:"public_builds,omitempty" json:"public_builds,omitempty"`
 439	OnlyAllowMergeIfPipelineSucceeds          *bool             `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
 440	OnlyAllowMergeIfAllDiscussionsAreResolved *bool             `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
 441	MergeMethod                               *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
 442	LFSEnabled                                *bool             `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
 443	RequestAccessEnabled                      *bool             `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
 444	TagList                                   *[]string         `url:"tag_list,omitempty" json:"tag_list,omitempty"`
 445	PrintingMergeRequestLinkEnabled           *bool             `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"`
 446	CIConfigPath                              *string           `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
 447	ApprovalsBeforeMerge                      *int              `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
 448	Mirror                                    *bool             `url:"mirror,omitempty" json:"mirror,omitempty"`
 449	MirrorTriggerBuilds                       *bool             `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
 450	InitializeWithReadme                      *bool             `url:"initialize_with_readme,omitempty" json:"initialize_with_readme,omitempty"`
 451}
 452
 453// CreateProject creates a new project owned by the authenticated user.
 454//
 455// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#create-project
 456func (s *ProjectsService) CreateProject(opt *CreateProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
 457	req, err := s.client.NewRequest("POST", "projects", opt, options)
 458	if err != nil {
 459		return nil, nil, err
 460	}
 461
 462	p := new(Project)
 463	resp, err := s.client.Do(req, p)
 464	if err != nil {
 465		return nil, resp, err
 466	}
 467
 468	return p, resp, err
 469}
 470
 471// CreateProjectForUserOptions represents the available CreateProjectForUser()
 472// options.
 473//
 474// GitLab API docs:
 475// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user
 476type CreateProjectForUserOptions CreateProjectOptions
 477
 478// CreateProjectForUser creates a new project owned by the specified user.
 479// Available only for admins.
 480//
 481// GitLab API docs:
 482// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user
 483func (s *ProjectsService) CreateProjectForUser(user int, opt *CreateProjectForUserOptions, options ...OptionFunc) (*Project, *Response, error) {
 484	u := fmt.Sprintf("projects/user/%d", user)
 485
 486	req, err := s.client.NewRequest("POST", u, opt, options)
 487	if err != nil {
 488		return nil, nil, err
 489	}
 490
 491	p := new(Project)
 492	resp, err := s.client.Do(req, p)
 493	if err != nil {
 494		return nil, resp, err
 495	}
 496
 497	return p, resp, err
 498}
 499
 500// EditProjectOptions represents the available EditProject() options.
 501//
 502// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
 503type EditProjectOptions struct {
 504	Name                                      *string           `url:"name,omitempty" json:"name,omitempty"`
 505	Path                                      *string           `url:"path,omitempty" json:"path,omitempty"`
 506	DefaultBranch                             *string           `url:"default_branch,omitempty" json:"default_branch,omitempty"`
 507	Description                               *string           `url:"description,omitempty" json:"description,omitempty"`
 508	IssuesEnabled                             *bool             `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
 509	MergeRequestsEnabled                      *bool             `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
 510	JobsEnabled                               *bool             `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
 511	WikiEnabled                               *bool             `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
 512	SnippetsEnabled                           *bool             `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
 513	ResolveOutdatedDiffDiscussions            *bool             `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
 514	ContainerRegistryEnabled                  *bool             `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
 515	SharedRunnersEnabled                      *bool             `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
 516	Visibility                                *VisibilityValue  `url:"visibility,omitempty" json:"visibility,omitempty"`
 517	ImportURL                                 *string           `url:"import_url,omitempty" json:"import_url,omitempty"`
 518	PublicBuilds                              *bool             `url:"public_builds,omitempty" json:"public_builds,omitempty"`
 519	OnlyAllowMergeIfPipelineSucceeds          *bool             `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
 520	OnlyAllowMergeIfAllDiscussionsAreResolved *bool             `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
 521	MergeMethod                               *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
 522	LFSEnabled                                *bool             `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
 523	RequestAccessEnabled                      *bool             `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
 524	TagList                                   *[]string         `url:"tag_list,omitempty" json:"tag_list,omitempty"`
 525	CIConfigPath                              *string           `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
 526	ApprovalsBeforeMerge                      *int              `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
 527	ExternalAuthorizationClassificationLabel  *string           `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"`
 528	Mirror                                    *bool             `url:"mirror,omitempty" json:"mirror,omitempty"`
 529	MirrorTriggerBuilds                       *bool             `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
 530	MirrorUserID                              *int              `url:"mirror_user_id,omitempty" json:"mirror_user_id,omitempty"`
 531	OnlyMirrorProtectedBranches               *bool             `url:"only_mirror_protected_branches,omitempty" json:"only_mirror_protected_branches,omitempty"`
 532	MirrorOverwritesDivergedBranches          *bool             `url:"mirror_overwrites_diverged_branches,omitempty" json:"mirror_overwrites_diverged_branches,omitempty"`
 533	PackagesEnabled                           *bool             `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"`
 534}
 535
 536// EditProject updates an existing project.
 537//
 538// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
 539func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
 540	project, err := parseID(pid)
 541	if err != nil {
 542		return nil, nil, err
 543	}
 544	u := fmt.Sprintf("projects/%s", pathEscape(project))
 545
 546	req, err := s.client.NewRequest("PUT", u, opt, options)
 547	if err != nil {
 548		return nil, nil, err
 549	}
 550
 551	p := new(Project)
 552	resp, err := s.client.Do(req, p)
 553	if err != nil {
 554		return nil, resp, err
 555	}
 556
 557	return p, resp, err
 558}
 559
 560// ForkProjectOptions represents the available ForkProject() options.
 561//
 562// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project
 563type ForkProjectOptions struct {
 564	Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"`
 565	Name      *string `url:"name,omitempty" json:"name,omitempty" `
 566	Path      *string `url:"path,omitempty" json:"path,omitempty"`
 567}
 568
 569// ForkProject forks a project into the user namespace of the authenticated
 570// user.
 571//
 572// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project
 573func (s *ProjectsService) ForkProject(pid interface{}, opt *ForkProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
 574	project, err := parseID(pid)
 575	if err != nil {
 576		return nil, nil, err
 577	}
 578	u := fmt.Sprintf("projects/%s/fork", pathEscape(project))
 579
 580	req, err := s.client.NewRequest("POST", u, opt, options)
 581	if err != nil {
 582		return nil, nil, err
 583	}
 584
 585	p := new(Project)
 586	resp, err := s.client.Do(req, p)
 587	if err != nil {
 588		return nil, resp, err
 589	}
 590
 591	return p, resp, err
 592}
 593
 594// StarProject stars a given the project.
 595//
 596// GitLab API docs:
 597// https://docs.gitlab.com/ce/api/projects.html#star-a-project
 598func (s *ProjectsService) StarProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
 599	project, err := parseID(pid)
 600	if err != nil {
 601		return nil, nil, err
 602	}
 603	u := fmt.Sprintf("projects/%s/star", pathEscape(project))
 604
 605	req, err := s.client.NewRequest("POST", u, nil, options)
 606	if err != nil {
 607		return nil, nil, err
 608	}
 609
 610	p := new(Project)
 611	resp, err := s.client.Do(req, p)
 612	if err != nil {
 613		return nil, resp, err
 614	}
 615
 616	return p, resp, err
 617}
 618
 619// UnstarProject unstars a given project.
 620//
 621// GitLab API docs:
 622// https://docs.gitlab.com/ce/api/projects.html#unstar-a-project
 623func (s *ProjectsService) UnstarProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
 624	project, err := parseID(pid)
 625	if err != nil {
 626		return nil, nil, err
 627	}
 628	u := fmt.Sprintf("projects/%s/unstar", pathEscape(project))
 629
 630	req, err := s.client.NewRequest("POST", u, nil, options)
 631	if err != nil {
 632		return nil, nil, err
 633	}
 634
 635	p := new(Project)
 636	resp, err := s.client.Do(req, p)
 637	if err != nil {
 638		return nil, resp, err
 639	}
 640
 641	return p, resp, err
 642}
 643
 644// ArchiveProject archives the project if the user is either admin or the
 645// project owner of this project.
 646//
 647// GitLab API docs:
 648// https://docs.gitlab.com/ce/api/projects.html#archive-a-project
 649func (s *ProjectsService) ArchiveProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
 650	project, err := parseID(pid)
 651	if err != nil {
 652		return nil, nil, err
 653	}
 654	u := fmt.Sprintf("projects/%s/archive", pathEscape(project))
 655
 656	req, err := s.client.NewRequest("POST", u, nil, options)
 657	if err != nil {
 658		return nil, nil, err
 659	}
 660
 661	p := new(Project)
 662	resp, err := s.client.Do(req, p)
 663	if err != nil {
 664		return nil, resp, err
 665	}
 666
 667	return p, resp, err
 668}
 669
 670// UnarchiveProject unarchives the project if the user is either admin or
 671// the project owner of this project.
 672//
 673// GitLab API docs:
 674// https://docs.gitlab.com/ce/api/projects.html#unarchive-a-project
 675func (s *ProjectsService) UnarchiveProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
 676	project, err := parseID(pid)
 677	if err != nil {
 678		return nil, nil, err
 679	}
 680	u := fmt.Sprintf("projects/%s/unarchive", pathEscape(project))
 681
 682	req, err := s.client.NewRequest("POST", u, nil, options)
 683	if err != nil {
 684		return nil, nil, err
 685	}
 686
 687	p := new(Project)
 688	resp, err := s.client.Do(req, p)
 689	if err != nil {
 690		return nil, resp, err
 691	}
 692
 693	return p, resp, err
 694}
 695
 696// DeleteProject removes a project including all associated resources
 697// (issues, merge requests etc.)
 698//
 699// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#remove-project
 700func (s *ProjectsService) DeleteProject(pid interface{}, options ...OptionFunc) (*Response, error) {
 701	project, err := parseID(pid)
 702	if err != nil {
 703		return nil, err
 704	}
 705	u := fmt.Sprintf("projects/%s", pathEscape(project))
 706
 707	req, err := s.client.NewRequest("DELETE", u, nil, options)
 708	if err != nil {
 709		return nil, err
 710	}
 711
 712	return s.client.Do(req, nil)
 713}
 714
 715// ShareWithGroupOptions represents options to share project with groups
 716//
 717// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group
 718type ShareWithGroupOptions struct {
 719	GroupID     *int              `url:"group_id" json:"group_id"`
 720	GroupAccess *AccessLevelValue `url:"group_access" json:"group_access"`
 721	ExpiresAt   *string           `url:"expires_at" json:"expires_at"`
 722}
 723
 724// ShareProjectWithGroup allows to share a project with a group.
 725//
 726// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group
 727func (s *ProjectsService) ShareProjectWithGroup(pid interface{}, opt *ShareWithGroupOptions, options ...OptionFunc) (*Response, error) {
 728	project, err := parseID(pid)
 729	if err != nil {
 730		return nil, err
 731	}
 732	u := fmt.Sprintf("projects/%s/share", pathEscape(project))
 733
 734	req, err := s.client.NewRequest("POST", u, opt, options)
 735	if err != nil {
 736		return nil, err
 737	}
 738
 739	return s.client.Do(req, nil)
 740}
 741
 742// DeleteSharedProjectFromGroup allows to unshare a project from a group.
 743//
 744// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#delete-a-shared-project-link-within-a-group
 745func (s *ProjectsService) DeleteSharedProjectFromGroup(pid interface{}, groupID int, options ...OptionFunc) (*Response, error) {
 746	project, err := parseID(pid)
 747	if err != nil {
 748		return nil, err
 749	}
 750	u := fmt.Sprintf("projects/%s/share/%d", pathEscape(project), groupID)
 751
 752	req, err := s.client.NewRequest("DELETE", u, nil, options)
 753	if err != nil {
 754		return nil, err
 755	}
 756
 757	return s.client.Do(req, nil)
 758}
 759
 760// ProjectMember represents a project member.
 761//
 762// GitLab API docs:
 763// https://docs.gitlab.com/ce/api/projects.html#list-project-team-members
 764type ProjectMember struct {
 765	ID          int              `json:"id"`
 766	Username    string           `json:"username"`
 767	Email       string           `json:"email"`
 768	Name        string           `json:"name"`
 769	State       string           `json:"state"`
 770	CreatedAt   *time.Time       `json:"created_at"`
 771	AccessLevel AccessLevelValue `json:"access_level"`
 772}
 773
 774// ProjectHook represents a project hook.
 775//
 776// GitLab API docs:
 777// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
 778type ProjectHook struct {
 779	ID                       int        `json:"id"`
 780	URL                      string     `json:"url"`
 781	ProjectID                int        `json:"project_id"`
 782	PushEvents               bool       `json:"push_events"`
 783	IssuesEvents             bool       `json:"issues_events"`
 784	ConfidentialIssuesEvents bool       `json:"confidential_issues_events"`
 785	MergeRequestsEvents      bool       `json:"merge_requests_events"`
 786	TagPushEvents            bool       `json:"tag_push_events"`
 787	NoteEvents               bool       `json:"note_events"`
 788	JobEvents                bool       `json:"job_events"`
 789	PipelineEvents           bool       `json:"pipeline_events"`
 790	WikiPageEvents           bool       `json:"wiki_page_events"`
 791	EnableSSLVerification    bool       `json:"enable_ssl_verification"`
 792	CreatedAt                *time.Time `json:"created_at"`
 793}
 794
 795// ListProjectHooksOptions represents the available ListProjectHooks() options.
 796//
 797// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
 798type ListProjectHooksOptions ListOptions
 799
 800// ListProjectHooks gets a list of project hooks.
 801//
 802// GitLab API docs:
 803// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
 804func (s *ProjectsService) ListProjectHooks(pid interface{}, opt *ListProjectHooksOptions, options ...OptionFunc) ([]*ProjectHook, *Response, error) {
 805	project, err := parseID(pid)
 806	if err != nil {
 807		return nil, nil, err
 808	}
 809	u := fmt.Sprintf("projects/%s/hooks", pathEscape(project))
 810
 811	req, err := s.client.NewRequest("GET", u, opt, options)
 812	if err != nil {
 813		return nil, nil, err
 814	}
 815
 816	var ph []*ProjectHook
 817	resp, err := s.client.Do(req, &ph)
 818	if err != nil {
 819		return nil, resp, err
 820	}
 821
 822	return ph, resp, err
 823}
 824
 825// GetProjectHook gets a specific hook for a project.
 826//
 827// GitLab API docs:
 828// https://docs.gitlab.com/ce/api/projects.html#get-project-hook
 829func (s *ProjectsService) GetProjectHook(pid interface{}, hook int, options ...OptionFunc) (*ProjectHook, *Response, error) {
 830	project, err := parseID(pid)
 831	if err != nil {
 832		return nil, nil, err
 833	}
 834	u := fmt.Sprintf("projects/%s/hooks/%d", pathEscape(project), hook)
 835
 836	req, err := s.client.NewRequest("GET", u, nil, options)
 837	if err != nil {
 838		return nil, nil, err
 839	}
 840
 841	ph := new(ProjectHook)
 842	resp, err := s.client.Do(req, ph)
 843	if err != nil {
 844		return nil, resp, err
 845	}
 846
 847	return ph, resp, err
 848}
 849
 850// AddProjectHookOptions represents the available AddProjectHook() options.
 851//
 852// GitLab API docs:
 853// https://docs.gitlab.com/ce/api/projects.html#add-project-hook
 854type AddProjectHookOptions struct {
 855	URL                      *string `url:"url,omitempty" json:"url,omitempty"`
 856	PushEvents               *bool   `url:"push_events,omitempty" json:"push_events,omitempty"`
 857	IssuesEvents             *bool   `url:"issues_events,omitempty" json:"issues_events,omitempty"`
 858	ConfidentialIssuesEvents *bool   `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
 859	MergeRequestsEvents      *bool   `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
 860	TagPushEvents            *bool   `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
 861	NoteEvents               *bool   `url:"note_events,omitempty" json:"note_events,omitempty"`
 862	JobEvents                *bool   `url:"job_events,omitempty" json:"job_events,omitempty"`
 863	PipelineEvents           *bool   `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
 864	WikiPageEvents           *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
 865	EnableSSLVerification    *bool   `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
 866	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
 867}
 868
 869// AddProjectHook adds a hook to a specified project.
 870//
 871// GitLab API docs:
 872// https://docs.gitlab.com/ce/api/projects.html#add-project-hook
 873func (s *ProjectsService) AddProjectHook(pid interface{}, opt *AddProjectHookOptions, options ...OptionFunc) (*ProjectHook, *Response, error) {
 874	project, err := parseID(pid)
 875	if err != nil {
 876		return nil, nil, err
 877	}
 878	u := fmt.Sprintf("projects/%s/hooks", pathEscape(project))
 879
 880	req, err := s.client.NewRequest("POST", u, opt, options)
 881	if err != nil {
 882		return nil, nil, err
 883	}
 884
 885	ph := new(ProjectHook)
 886	resp, err := s.client.Do(req, ph)
 887	if err != nil {
 888		return nil, resp, err
 889	}
 890
 891	return ph, resp, err
 892}
 893
 894// EditProjectHookOptions represents the available EditProjectHook() options.
 895//
 896// GitLab API docs:
 897// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook
 898type EditProjectHookOptions struct {
 899	URL                      *string `url:"url,omitempty" json:"url,omitempty"`
 900	PushEvents               *bool   `url:"push_events,omitempty" json:"push_events,omitempty"`
 901	IssuesEvents             *bool   `url:"issues_events,omitempty" json:"issues_events,omitempty"`
 902	ConfidentialIssuesEvents *bool   `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
 903	MergeRequestsEvents      *bool   `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
 904	TagPushEvents            *bool   `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
 905	NoteEvents               *bool   `url:"note_events,omitempty" json:"note_events,omitempty"`
 906	JobEvents                *bool   `url:"job_events,omitempty" json:"job_events,omitempty"`
 907	PipelineEvents           *bool   `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
 908	WikiPageEvents           *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
 909	EnableSSLVerification    *bool   `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
 910	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
 911}
 912
 913// EditProjectHook edits a hook for a specified project.
 914//
 915// GitLab API docs:
 916// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook
 917func (s *ProjectsService) EditProjectHook(pid interface{}, hook int, opt *EditProjectHookOptions, options ...OptionFunc) (*ProjectHook, *Response, error) {
 918	project, err := parseID(pid)
 919	if err != nil {
 920		return nil, nil, err
 921	}
 922	u := fmt.Sprintf("projects/%s/hooks/%d", pathEscape(project), hook)
 923
 924	req, err := s.client.NewRequest("PUT", u, opt, options)
 925	if err != nil {
 926		return nil, nil, err
 927	}
 928
 929	ph := new(ProjectHook)
 930	resp, err := s.client.Do(req, ph)
 931	if err != nil {
 932		return nil, resp, err
 933	}
 934
 935	return ph, resp, err
 936}
 937
 938// DeleteProjectHook removes a hook from a project. This is an idempotent
 939// method and can be called multiple times. Either the hook is available or not.
 940//
 941// GitLab API docs:
 942// https://docs.gitlab.com/ce/api/projects.html#delete-project-hook
 943func (s *ProjectsService) DeleteProjectHook(pid interface{}, hook int, options ...OptionFunc) (*Response, error) {
 944	project, err := parseID(pid)
 945	if err != nil {
 946		return nil, err
 947	}
 948	u := fmt.Sprintf("projects/%s/hooks/%d", pathEscape(project), hook)
 949
 950	req, err := s.client.NewRequest("DELETE", u, nil, options)
 951	if err != nil {
 952		return nil, err
 953	}
 954
 955	return s.client.Do(req, nil)
 956}
 957
 958// ProjectForkRelation represents a project fork relationship.
 959//
 960// GitLab API docs:
 961// https://docs.gitlab.com/ce/api/projects.html#admin-fork-relation
 962type ProjectForkRelation struct {
 963	ID                  int        `json:"id"`
 964	ForkedToProjectID   int        `json:"forked_to_project_id"`
 965	ForkedFromProjectID int        `json:"forked_from_project_id"`
 966	CreatedAt           *time.Time `json:"created_at"`
 967	UpdatedAt           *time.Time `json:"updated_at"`
 968}
 969
 970// CreateProjectForkRelation creates a forked from/to relation between
 971// existing projects.
 972//
 973// GitLab API docs:
 974// https://docs.gitlab.com/ce/api/projects.html#create-a-forked-fromto-relation-between-existing-projects.
 975func (s *ProjectsService) CreateProjectForkRelation(pid int, fork int, options ...OptionFunc) (*ProjectForkRelation, *Response, error) {
 976	u := fmt.Sprintf("projects/%d/fork/%d", pid, fork)
 977
 978	req, err := s.client.NewRequest("POST", u, nil, options)
 979	if err != nil {
 980		return nil, nil, err
 981	}
 982
 983	pfr := new(ProjectForkRelation)
 984	resp, err := s.client.Do(req, pfr)
 985	if err != nil {
 986		return nil, resp, err
 987	}
 988
 989	return pfr, resp, err
 990}
 991
 992// DeleteProjectForkRelation deletes an existing forked from relationship.
 993//
 994// GitLab API docs:
 995// https://docs.gitlab.com/ce/api/projects.html#delete-an-existing-forked-from-relationship
 996func (s *ProjectsService) DeleteProjectForkRelation(pid int, options ...OptionFunc) (*Response, error) {
 997	u := fmt.Sprintf("projects/%d/fork", pid)
 998
 999	req, err := s.client.NewRequest("DELETE", u, nil, options)
1000	if err != nil {
1001		return nil, err
1002	}
1003
1004	return s.client.Do(req, nil)
1005}
1006
1007// ProjectFile represents an uploaded project file
1008//
1009// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
1010type ProjectFile struct {
1011	Alt      string `json:"alt"`
1012	URL      string `json:"url"`
1013	Markdown string `json:"markdown"`
1014}
1015
1016// UploadFile upload a file from disk
1017//
1018// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
1019func (s *ProjectsService) UploadFile(pid interface{}, file string, options ...OptionFunc) (*ProjectFile, *Response, error) {
1020	project, err := parseID(pid)
1021	if err != nil {
1022		return nil, nil, err
1023	}
1024	u := fmt.Sprintf("projects/%s/uploads", pathEscape(project))
1025
1026	f, err := os.Open(file)
1027	if err != nil {
1028		return nil, nil, err
1029	}
1030	defer f.Close()
1031
1032	b := &bytes.Buffer{}
1033	w := multipart.NewWriter(b)
1034
1035	fw, err := w.CreateFormFile("file", file)
1036	if err != nil {
1037		return nil, nil, err
1038	}
1039
1040	_, err = io.Copy(fw, f)
1041	if err != nil {
1042		return nil, nil, err
1043	}
1044	w.Close()
1045
1046	req, err := s.client.NewRequest("", u, nil, options)
1047	if err != nil {
1048		return nil, nil, err
1049	}
1050
1051	req.Body = ioutil.NopCloser(b)
1052	req.ContentLength = int64(b.Len())
1053	req.Header.Set("Content-Type", w.FormDataContentType())
1054	req.Method = "POST"
1055
1056	uf := &ProjectFile{}
1057	resp, err := s.client.Do(req, uf)
1058	if err != nil {
1059		return nil, resp, err
1060	}
1061
1062	return uf, resp, nil
1063}
1064
1065// ListProjectForks gets a list of project forks.
1066//
1067// GitLab API docs:
1068// https://docs.gitlab.com/ce/api/projects.html#list-forks-of-a-project
1069func (s *ProjectsService) ListProjectForks(pid interface{}, opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
1070	project, err := parseID(pid)
1071	if err != nil {
1072		return nil, nil, err
1073	}
1074	u := fmt.Sprintf("projects/%s/forks", pathEscape(project))
1075
1076	req, err := s.client.NewRequest("GET", u, opt, options)
1077	if err != nil {
1078		return nil, nil, err
1079	}
1080
1081	var forks []*Project
1082	resp, err := s.client.Do(req, &forks)
1083	if err != nil {
1084		return nil, resp, err
1085	}
1086
1087	return forks, resp, err
1088}
1089
1090// ProjectPushRules represents a project push rule.
1091//
1092// GitLab API docs:
1093// https://docs.gitlab.com/ee/api/projects.html#push-rules
1094type ProjectPushRules struct {
1095	ID                 int        `json:"id"`
1096	ProjectID          int        `json:"project_id"`
1097	CommitMessageRegex string     `json:"commit_message_regex"`
1098	BranchNameRegex    string     `json:"branch_name_regex"`
1099	DenyDeleteTag      bool       `json:"deny_delete_tag"`
1100	CreatedAt          *time.Time `json:"created_at"`
1101	MemberCheck        bool       `json:"member_check"`
1102	PreventSecrets     bool       `json:"prevent_secrets"`
1103	AuthorEmailRegex   string     `json:"author_email_regex"`
1104	FileNameRegex      string     `json:"file_name_regex"`
1105	MaxFileSize        int        `json:"max_file_size"`
1106}
1107
1108// GetProjectPushRules gets the push rules of a project.
1109//
1110// GitLab API docs:
1111// https://docs.gitlab.com/ee/api/projects.html#get-project-push-rules
1112func (s *ProjectsService) GetProjectPushRules(pid interface{}, options ...OptionFunc) (*ProjectPushRules, *Response, error) {
1113	project, err := parseID(pid)
1114	if err != nil {
1115		return nil, nil, err
1116	}
1117	u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1118
1119	req, err := s.client.NewRequest("GET", u, nil, options)
1120	if err != nil {
1121		return nil, nil, err
1122	}
1123
1124	ppr := new(ProjectPushRules)
1125	resp, err := s.client.Do(req, ppr)
1126	if err != nil {
1127		return nil, resp, err
1128	}
1129
1130	return ppr, resp, err
1131}
1132
1133// AddProjectPushRuleOptions represents the available AddProjectPushRule()
1134// options.
1135//
1136// GitLab API docs:
1137// https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule
1138type AddProjectPushRuleOptions struct {
1139	DenyDeleteTag      *bool   `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"`
1140	MemberCheck        *bool   `url:"member_check,omitempty" json:"member_check,omitempty"`
1141	PreventSecrets     *bool   `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"`
1142	CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"`
1143	BranchNameRegex    *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"`
1144	AuthorEmailRegex   *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"`
1145	FileNameRegex      *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"`
1146	MaxFileSize        *int    `url:"max_file_size,omitempty" json:"max_file_size,omitempty"`
1147}
1148
1149// AddProjectPushRule adds a push rule to a specified project.
1150//
1151// GitLab API docs:
1152// https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule
1153func (s *ProjectsService) AddProjectPushRule(pid interface{}, opt *AddProjectPushRuleOptions, options ...OptionFunc) (*ProjectPushRules, *Response, error) {
1154	project, err := parseID(pid)
1155	if err != nil {
1156		return nil, nil, err
1157	}
1158	u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1159
1160	req, err := s.client.NewRequest("POST", u, opt, options)
1161	if err != nil {
1162		return nil, nil, err
1163	}
1164
1165	ppr := new(ProjectPushRules)
1166	resp, err := s.client.Do(req, ppr)
1167	if err != nil {
1168		return nil, resp, err
1169	}
1170
1171	return ppr, resp, err
1172}
1173
1174// EditProjectPushRuleOptions represents the available EditProjectPushRule()
1175// options.
1176//
1177// GitLab API docs:
1178// https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule
1179type EditProjectPushRuleOptions struct {
1180	AuthorEmailRegex   *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"`
1181	BranchNameRegex    *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"`
1182	CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"`
1183	FileNameRegex      *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"`
1184	DenyDeleteTag      *bool   `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"`
1185	MemberCheck        *bool   `url:"member_check,omitempty" json:"member_check,omitempty"`
1186	PreventSecrets     *bool   `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"`
1187	MaxFileSize        *int    `url:"max_file_size,omitempty" json:"max_file_size,omitempty"`
1188}
1189
1190// EditProjectPushRule edits a push rule for a specified project.
1191//
1192// GitLab API docs:
1193// https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule
1194func (s *ProjectsService) EditProjectPushRule(pid interface{}, opt *EditProjectPushRuleOptions, options ...OptionFunc) (*ProjectPushRules, *Response, error) {
1195	project, err := parseID(pid)
1196	if err != nil {
1197		return nil, nil, err
1198	}
1199	u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1200
1201	req, err := s.client.NewRequest("PUT", u, opt, options)
1202	if err != nil {
1203		return nil, nil, err
1204	}
1205
1206	ppr := new(ProjectPushRules)
1207	resp, err := s.client.Do(req, ppr)
1208	if err != nil {
1209		return nil, resp, err
1210	}
1211
1212	return ppr, resp, err
1213}
1214
1215// DeleteProjectPushRule removes a push rule from a project. This is an
1216// idempotent method and can be called multiple times. Either the push rule is
1217// available or not.
1218//
1219// GitLab API docs:
1220// https://docs.gitlab.com/ee/api/projects.html#delete-project-push-rule
1221func (s *ProjectsService) DeleteProjectPushRule(pid interface{}, options ...OptionFunc) (*Response, error) {
1222	project, err := parseID(pid)
1223	if err != nil {
1224		return nil, err
1225	}
1226	u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1227
1228	req, err := s.client.NewRequest("DELETE", u, nil, options)
1229	if err != nil {
1230		return nil, err
1231	}
1232
1233	return s.client.Do(req, nil)
1234}
1235
1236// ProjectApprovals represents GitLab project level merge request approvals.
1237//
1238// GitLab API docs:
1239// https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals
1240type ProjectApprovals struct {
1241	Approvers                                 []*MergeRequestApproverUser  `json:"approvers"`
1242	ApproverGroups                            []*MergeRequestApproverGroup `json:"approver_groups"`
1243	ApprovalsBeforeMerge                      int                          `json:"approvals_before_merge"`
1244	ResetApprovalsOnPush                      bool                         `json:"reset_approvals_on_push"`
1245	DisableOverridingApproversPerMergeRequest bool                         `json:"disable_overriding_approvers_per_merge_request"`
1246	MergeRequestsAuthorApproval               bool                         `json:"merge_requests_author_approval"`
1247}
1248
1249// GetApprovalConfiguration get the approval configuration for a project.
1250//
1251// GitLab API docs:
1252// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration
1253func (s *ProjectsService) GetApprovalConfiguration(pid interface{}, options ...OptionFunc) (*ProjectApprovals, *Response, error) {
1254	project, err := parseID(pid)
1255	if err != nil {
1256		return nil, nil, err
1257	}
1258	u := fmt.Sprintf("projects/%s/approvals", pathEscape(project))
1259
1260	req, err := s.client.NewRequest("GET", u, nil, options)
1261	if err != nil {
1262		return nil, nil, err
1263	}
1264
1265	pa := new(ProjectApprovals)
1266	resp, err := s.client.Do(req, pa)
1267	if err != nil {
1268		return nil, resp, err
1269	}
1270
1271	return pa, resp, err
1272}
1273
1274// ChangeApprovalConfigurationOptions represents the available
1275// ApprovalConfiguration() options.
1276//
1277// GitLab API docs:
1278// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration
1279type ChangeApprovalConfigurationOptions struct {
1280	ApprovalsBeforeMerge                      *int  `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
1281	ResetApprovalsOnPush                      *bool `url:"reset_approvals_on_push,omitempty" json:"reset_approvals_on_push,omitempty"`
1282	DisableOverridingApproversPerMergeRequest *bool `url:"disable_overriding_approvers_per_merge_request,omitempty" json:"disable_overriding_approvers_per_merge_request,omitempty"`
1283	MergeRequestsAuthorApproval               *bool `url:"merge_requests_author_approval,omitempty" json:"merge_requests_author_approval,omitempty"`
1284}
1285
1286// ChangeApprovalConfiguration updates the approval configuration for a project.
1287//
1288// GitLab API docs:
1289// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration
1290func (s *ProjectsService) ChangeApprovalConfiguration(pid interface{}, opt *ChangeApprovalConfigurationOptions, options ...OptionFunc) (*ProjectApprovals, *Response, error) {
1291	project, err := parseID(pid)
1292	if err != nil {
1293		return nil, nil, err
1294	}
1295	u := fmt.Sprintf("projects/%s/approvals", pathEscape(project))
1296
1297	req, err := s.client.NewRequest("POST", u, opt, options)
1298	if err != nil {
1299		return nil, nil, err
1300	}
1301
1302	pa := new(ProjectApprovals)
1303	resp, err := s.client.Do(req, pa)
1304	if err != nil {
1305		return nil, resp, err
1306	}
1307
1308	return pa, resp, err
1309}
1310
1311// ChangeAllowedApproversOptions represents the available ChangeAllowedApprovers()
1312// options.
1313//
1314// GitLab API docs:
1315// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers
1316type ChangeAllowedApproversOptions struct {
1317	ApproverIDs      []*int `url:"approver_ids,omitempty" json:"approver_ids,omitempty"`
1318	ApproverGroupIDs []*int `url:"approver_group_ids,omitempty" json:"approver_group_ids,omitempty"`
1319}
1320
1321// ChangeAllowedApprovers updates the list of approvers and approver groups.
1322//
1323// GitLab API docs:
1324// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers
1325func (s *ProjectsService) ChangeAllowedApprovers(pid interface{}, opt *ChangeAllowedApproversOptions, options ...OptionFunc) (*ProjectApprovals, *Response, error) {
1326	project, err := parseID(pid)
1327	if err != nil {
1328		return nil, nil, err
1329	}
1330	u := fmt.Sprintf("projects/%s/approvers", pathEscape(project))
1331
1332	req, err := s.client.NewRequest("POST", u, opt, options)
1333	if err != nil {
1334		return nil, nil, err
1335	}
1336
1337	pa := new(ProjectApprovals)
1338	resp, err := s.client.Do(req, pa)
1339	if err != nil {
1340		return nil, resp, err
1341	}
1342
1343	return pa, resp, err
1344}