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