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