diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 417fee3ba..32bd82274 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,8 +4,7 @@ We want to make contributing to this project as easy as possible. ## Reporting Issues -If you have an issue, please report it on the [issue tracker]( -https://github.com/xanzy/go-gitlab/issues). +If you have an issue, please report it on the [issue tracker](https://github.com/xanzy/go-gitlab/issues). When you are up for writing a PR to solve the issue you encountered, it's not needed to first open a separate issue. In that case only opening a PR with a @@ -22,8 +21,10 @@ this project only supports what is in the public API docs. ## Coding style -We try to follow the Go best practices, where it makes sense, and use [`gofumpt`]( -https://github.com/mvdan/gofumpt) to format code in this project. +We try to follow the Go best practices, where it makes sense, and use +[`gofumpt`](https://github.com/mvdan/gofumpt) to format code in this project. +As a general rule of thumb we prefer to keep line width for comments below 80 +chars and for code (where possible and sensible) below 100 chars. Before making a PR, please look at the rest this package and try to make sure your contribution is consistent with the rest of the coding style. @@ -35,18 +36,18 @@ easier to find things. ### Setting up your local development environment to Contribute to `go-gitlab` 1. [Fork](https://github.com/xanzy/go-gitlab/fork), then clone the repository. - ```sh - git clone https://github.com//go-gitlab.git - # or via ssh - git clone git@github.com:/go-gitlab.git - ``` + ```sh + git clone https://github.com//go-gitlab.git + # or via ssh + git clone git@github.com:/go-gitlab.git + ``` 1. Install dependencies: - ```sh - make setup - ``` + ```sh + make setup + ``` 1. Make your changes on your feature branch 1. Run the tests and `gofumpt` - ```sh - make test && make fmt - ``` + ```sh + make test && make fmt + ``` 1. Open up your pull request diff --git a/gitlab.go b/gitlab.go index 10e5426b4..658628881 100644 --- a/gitlab.go +++ b/gitlab.go @@ -144,6 +144,7 @@ type Client struct { GroupLabels *GroupLabelsService GroupMembers *GroupMembersService GroupMilestones *GroupMilestonesService + GroupProtectedEnvironments *GroupProtectedEnvironmentsService GroupRepositoryStorageMove *GroupRepositoryStorageMoveService GroupVariables *GroupVariablesService GroupWikis *GroupWikisService @@ -366,6 +367,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.GroupLabels = &GroupLabelsService{client: c} c.GroupMembers = &GroupMembersService{client: c} c.GroupMilestones = &GroupMilestonesService{client: c} + c.GroupProtectedEnvironments = &GroupProtectedEnvironmentsService{client: c} c.GroupRepositoryStorageMove = &GroupRepositoryStorageMoveService{client: c} c.GroupVariables = &GroupVariablesService{client: c} c.GroupWikis = &GroupWikisService{client: c} diff --git a/group_protected_environments.go b/group_protected_environments.go new file mode 100644 index 000000000..53bee7079 --- /dev/null +++ b/group_protected_environments.go @@ -0,0 +1,278 @@ +// +// Copyright 2023, Sander van Harmelen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "fmt" + "net/http" +) + +// GroupProtectedEnvironmentsService handles communication with the group-level +// protected environment methods of the GitLab API. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html +type GroupProtectedEnvironmentsService struct { + client *Client +} + +// GroupProtectedEnvironment represents a group-level protected environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html +type GroupProtectedEnvironment struct { + Name string `json:"name"` + DeployAccessLevels []*GroupEnvironmentAccessDescription `json:"deploy_access_levels"` + RequiredApprovalCount int `json:"required_approval_count"` + ApprovalRules []*GroupEnvironmentApprovalRule `json:"approval_rules"` +} + +// GroupEnvironmentAccessDescription represents the access decription for a +// group-level protected environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html +type GroupEnvironmentAccessDescription struct { + ID int `json:"id"` + AccessLevel AccessLevelValue `json:"access_level"` + AccessLevelDescription string `json:"access_level_description"` + UserID int `json:"user_id"` + GroupID int `json:"group_id"` +} + +// GroupEnvironmentApprovalRule represents the approval rules for a group-level +// protected environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment +type GroupEnvironmentApprovalRule struct { + ID int `json:"id"` + UserID int `json:"user_id"` + GroupID int `json:"group_id"` + AccessLevel AccessLevelValue `json:"access_level"` + AccessLevelDescription string `json:"access_level_description"` + RequiredApprovalCount int `json:"required_approvals"` + GroupInheritanceType int `json:"group_inheritance_type"` +} + +// ListGroupProtectedEnvironmentsOptions represents the available +// ListGroupProtectedEnvironments() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#list-group-level-protected-environments +type ListGroupProtectedEnvironmentsOptions ListOptions + +// ListGroupProtectedEnvironments returns a list of protected environments from +// a group. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#list-group-level-protected-environments +func (s *GroupProtectedEnvironmentsService) ListGroupProtectedEnvironments(gid interface{}, opt *ListGroupProtectedEnvironmentsOptions, options ...RequestOptionFunc) ([]*GroupProtectedEnvironment, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/protected_environments", PathEscape(group)) + + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var pes []*GroupProtectedEnvironment + resp, err := s.client.Do(req, &pes) + if err != nil { + return nil, resp, err + } + + return pes, resp, nil +} + +// GetGroupProtectedEnvironment returns a single group-level protected +// environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#get-a-single-protected-environment +func (s *GroupProtectedEnvironmentsService) GetGroupProtectedEnvironment(gid interface{}, environment string, options ...RequestOptionFunc) (*GroupProtectedEnvironment, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/protected_environments/%s", PathEscape(group), environment) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + pe := new(GroupProtectedEnvironment) + resp, err := s.client.Do(req, pe) + if err != nil { + return nil, resp, err + } + + return pe, resp, nil +} + +// ProtectGroupEnvironmentOptions represents the available +// ProtectGroupEnvironment() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment +type ProtectGroupEnvironmentOptions struct { + Name *string `url:"name,omitempty" json:"name,omitempty"` + DeployAccessLevels *[]*GroupEnvironmentAccessOptions `url:"deploy_access_levels,omitempty" json:"deploy_access_levels,omitempty"` + RequiredApprovalCount *int `url:"required_approval_count,omitempty" json:"required_approval_count,omitempty"` + ApprovalRules *[]*GroupEnvironmentApprovalRuleOptions `url:"approval_rules,omitempty" json:"approval_rules,omitempty"` +} + +// GroupEnvironmentAccessOptions represents the options for an access decription +// for a group-level protected environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment +type GroupEnvironmentAccessOptions struct { + AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` + UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` + GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` +} + +// GroupEnvironmentApprovalRuleOptions represents the approval rules for a +// group-level protected environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment +type GroupEnvironmentApprovalRuleOptions struct { + UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` + GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` + AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` + AccessLevelDescription *string `url:"access_level_description,omitempty" json:"access_level_description,omitempty"` + RequiredApprovalCount *int `url:"required_approvals,omitempty" json:"required_approvals,omitempty"` + GroupInheritanceType *int `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"` +} + +// ProtectGroupEnvironment protects a single group-level environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment +func (s *GroupProtectedEnvironmentsService) ProtectGroupEnvironment(gid interface{}, opt *ProtectGroupEnvironmentOptions, options ...RequestOptionFunc) (*GroupProtectedEnvironment, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/protected_environments", PathEscape(group)) + + req, err := s.client.NewRequest(http.MethodPost, u, opt, options) + if err != nil { + return nil, nil, err + } + + pe := new(GroupProtectedEnvironment) + resp, err := s.client.Do(req, pe) + if err != nil { + return nil, resp, err + } + + return pe, resp, nil +} + +// UpdateGroupProtectedEnvironmentOptions represents the available +// UpdateGroupProtectedEnvironment() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment +type UpdateGroupProtectedEnvironmentOptions struct { + Name *string `url:"name,omitempty" json:"name,omitempty"` + DeployAccessLevels *[]*UpdateGroupEnvironmentAccessOptions `url:"deploy_access_levels,omitempty" json:"deploy_access_levels,omitempty"` + RequiredApprovalCount *int `url:"required_approval_count,omitempty" json:"required_approval_count,omitempty"` + ApprovalRules *[]*UpdateGroupEnvironmentApprovalRuleOptions `url:"approval_rules,omitempty" json:"approval_rules,omitempty"` +} + +// UpdateGroupEnvironmentAccessOptions represents the options for updates to the +// access decription for a group-level protected environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment +type UpdateGroupEnvironmentAccessOptions struct { + AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` + ID *int `url:"id,omitempty" json:"id,omitempty"` + UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` + GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` + Destroy *bool `url:"_destroy,omitempty" json:"_destroy,omitempty"` +} + +// UpdateGroupEnvironmentApprovalRuleOptions represents the updates to the +// approval rules for a group-level protected environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment +type UpdateGroupEnvironmentApprovalRuleOptions struct { + ID *int `url:"id,omitempty" json:"id,omitempty"` + UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` + GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` + AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` + AccessLevelDescription *string `url:"access_level_description,omitempty" json:"access_level_description,omitempty"` + RequiredApprovalCount *int `url:"required_approvals,omitempty" json:"required_approvals,omitempty"` + GroupInheritanceType *int `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"` + Destroy *bool `url:"_destroy,omitempty" json:"_destroy,omitempty"` +} + +// UpdateGroupProtectedEnvironment updates a single group-level protected +// environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment +func (s *GroupProtectedEnvironmentsService) UpdateGroupProtectedEnvironment(gid interface{}, environment string, opt *UpdateGroupProtectedEnvironmentOptions, options ...RequestOptionFunc) (*GroupProtectedEnvironment, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/protected_environments/%s", PathEscape(group), environment) + + req, err := s.client.NewRequest(http.MethodPut, u, opt, options) + if err != nil { + return nil, nil, err + } + + pe := new(GroupProtectedEnvironment) + resp, err := s.client.Do(req, pe) + if err != nil { + return nil, resp, err + } + + return pe, resp, nil +} + +// UnprotectGroupEnvironment unprotects the given protected group-level +// environment. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_protected_environments.html#unprotect-a-single-environment +func (s *GroupProtectedEnvironmentsService) UnprotectGroupEnvironment(gid interface{}, environment string, options ...RequestOptionFunc) (*Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, err + } + u := fmt.Sprintf("groups/%s/protected_environments/%s", PathEscape(group), environment) + + req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/group_protected_environments_test.go b/group_protected_environments_test.go new file mode 100644 index 000000000..b8ede66f6 --- /dev/null +++ b/group_protected_environments_test.go @@ -0,0 +1,574 @@ +// +// Copyright 2023, Sander van Harmelen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "fmt" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGroupListProtectedEnvironments(t *testing.T) { + mux, client := setup(t) + + mux.HandleFunc("/api/v4/groups/1/protected_environments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, `[{ + "name":"staging", + "deploy_access_levels": [ + { + "access_level": 40, + "access_level_description": "Maintainers" + } + ], + "required_approval_count": 1, + "approval_rules": [ + { + "id": 38, + "user_id": 42, + "group_id": null, + "access_level": null, + "access_level_description": "qa-group", + "required_approvals": 1, + "group_inheritance_type": 0 + }, + { + "id": 39, + "user_id": null, + "group_id": 135, + "access_level": 30, + "access_level_description": "security-group", + "required_approvals": 2, + "group_inheritance_type": 1 + } + ] + },{ + "name":"production", + "deploy_access_levels": [ + { + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ] + }]`) + }) + + expected := []*GroupProtectedEnvironment{ + { + Name: "staging", + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + AccessLevel: 40, + AccessLevelDescription: "Maintainers", + }, + }, + RequiredApprovalCount: 1, + ApprovalRules: []*GroupEnvironmentApprovalRule{ + { + ID: 38, + UserID: 42, + AccessLevelDescription: "qa-group", + RequiredApprovalCount: 1, + }, + { + ID: 39, + GroupID: 135, + AccessLevel: 30, + AccessLevelDescription: "security-group", + RequiredApprovalCount: 2, + GroupInheritanceType: 1, + }, + }, + }, + { + Name: "production", + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + }, + } + + opt := &ListGroupProtectedEnvironmentsOptions{} + environments, _, err := client.GroupProtectedEnvironments.ListGroupProtectedEnvironments(1, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environments) +} + +func TestGroupGetProtectedEnvironment(t *testing.T) { + mux, client := setup(t) + + // Test with RequiredApprovalCount + environmentName := "development" + + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/1/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ], + "required_approval_count": 1, + "approval_rules": [ + { + "id": 1, + "user_id": null, + "group_id": 10, + "access_level": 5, + "access_level_description": "devops", + "required_approvals": 0, + "group_inheritance_type": 0 + } + ] + }`, environmentName) + }) + + expected := &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + RequiredApprovalCount: 1, + ApprovalRules: []*GroupEnvironmentApprovalRule{ + { + ID: 1, + GroupID: 10, + AccessLevel: 5, + AccessLevelDescription: "devops", + }, + }, + } + + environment, _, err := client.GroupProtectedEnvironments.GetGroupProtectedEnvironment(1, environmentName) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) + + // Test without RequiredApprovalCount nor ApprovalRules + environmentName = "testing" + + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/2/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ] + }`, environmentName) + }) + + expected = &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + } + + environment, _, err = client.GroupProtectedEnvironments.GetGroupProtectedEnvironment(2, environmentName) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) +} + +func TestGroupProtectEnvironments(t *testing.T) { + mux, client := setup(t) + + // Test with RequiredApprovalCount and ApprovalRules + environmentName := "other" + + mux.HandleFunc("/api/v4/groups/1/protected_environments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ], + "required_approval_count": 2, + "approval_rules": [ + { + "id": 1, + "user_id": null, + "group_id": 10, + "access_level": 5, + "access_level_description": "devops", + "required_approvals": 0, + "group_inheritance_type": 0 + } + ] + }`, environmentName) + }) + + expected := &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + RequiredApprovalCount: 2, + ApprovalRules: []*GroupEnvironmentApprovalRule{ + { + ID: 1, + GroupID: 10, + AccessLevel: 5, + AccessLevelDescription: "devops", + }, + }, + } + + opt := &ProtectGroupEnvironmentOptions{ + Name: String(environmentName), + DeployAccessLevels: &[]*GroupEnvironmentAccessOptions{ + {AccessLevel: AccessLevel(30)}, + }, + RequiredApprovalCount: Int(2), + ApprovalRules: &[]*GroupEnvironmentApprovalRuleOptions{ + { + GroupID: Int(10), + AccessLevel: AccessLevel(0), + AccessLevelDescription: String("devops"), + }, + }, + } + + environment, _, err := client.GroupProtectedEnvironments.ProtectGroupEnvironment(1, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) + + // Test without RequiredApprovalCount nor ApprovalRules + environmentName = "staging" + + mux.HandleFunc("/api/v4/groups/2/protected_environments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ] + }`, environmentName) + }) + + expected = &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + } + + opt = &ProtectGroupEnvironmentOptions{ + Name: String(environmentName), + DeployAccessLevels: &[]*GroupEnvironmentAccessOptions{ + {AccessLevel: AccessLevel(30)}, + }, + } + environment, _, err = client.GroupProtectedEnvironments.ProtectGroupEnvironment(2, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) +} + +func TestGroupUpdateProtectedEnvironments(t *testing.T) { + mux, client := setup(t) + + // Test with DeployAccessLevels, RequiredApprovalCount, and ApprovalRules as if adding new to existing protected environment + environmentName := "other" + + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/1/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPut) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "id": 42, + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ], + "required_approval_count": 2, + "approval_rules": [ + { + "id": 1, + "user_id": null, + "group_id": 10, + "access_level": 5, + "access_level_description": "devops", + "required_approvals": 0, + "group_inheritance_type": 0 + } + ] + }`, environmentName) + }) + + expected := &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + ID: 42, + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + RequiredApprovalCount: 2, + ApprovalRules: []*GroupEnvironmentApprovalRule{ + { + ID: 1, + GroupID: 10, + AccessLevel: 5, + AccessLevelDescription: "devops", + }, + }, + } + + opt := &UpdateGroupProtectedEnvironmentOptions{ + Name: String(environmentName), + DeployAccessLevels: &[]*UpdateGroupEnvironmentAccessOptions{ + {AccessLevel: AccessLevel(30)}, + }, + RequiredApprovalCount: Int(2), + ApprovalRules: &[]*UpdateGroupEnvironmentApprovalRuleOptions{ + { + GroupID: Int(10), + AccessLevel: AccessLevel(0), + AccessLevelDescription: String("devops"), + }, + }, + } + + environment, _, err := client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(1, environmentName, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) + + // Test with DeployAccessLevels only, as if adding new to existing protected environment + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/2/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPut) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "id": 42, + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ] + }`, environmentName) + }) + + expected = &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + ID: 42, + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + } + + opt = &UpdateGroupProtectedEnvironmentOptions{ + Name: String(environmentName), + DeployAccessLevels: &[]*UpdateGroupEnvironmentAccessOptions{ + {AccessLevel: AccessLevel(30)}, + }, + } + environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(2, environmentName, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) + + // Test update to DeployAccessLevel + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/3/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPut) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "id": 42, + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ], + "required_approval_count": 2 + }`, environmentName) + }) + + expected = &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + ID: 42, + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + RequiredApprovalCount: 2, + } + + opt = &UpdateGroupProtectedEnvironmentOptions{ + Name: String(environmentName), + DeployAccessLevels: &[]*UpdateGroupEnvironmentAccessOptions{ + { + ID: Int(42), + AccessLevel: AccessLevel(30), + }, + }, + } + environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(3, environmentName, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) + + // Test update to ApprovalRules + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/4/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPut) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "id": 42, + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ], + "required_approval_count": 2, + "approval_rules": [ + { + "id": 1, + "user_id": null, + "group_id": 10, + "access_level": 5, + "access_level_description": "devops", + "required_approvals": 0, + "group_inheritance_type": 0 + } + ] + }`, environmentName) + }) + + expected = &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + ID: 42, + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + RequiredApprovalCount: 2, + ApprovalRules: []*GroupEnvironmentApprovalRule{ + { + ID: 1, + GroupID: 10, + AccessLevel: 5, + AccessLevelDescription: "devops", + }, + }, + } + + opt = &UpdateGroupProtectedEnvironmentOptions{ + Name: String(environmentName), + ApprovalRules: &[]*UpdateGroupEnvironmentApprovalRuleOptions{ + { + ID: Int(1), + GroupID: Int(10), + AccessLevel: AccessLevel(0), + AccessLevelDescription: String("devops"), + }, + }, + } + + environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(4, environmentName, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) + + // Test destroy ApprovalRule + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/5/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPut) + fmt.Fprintf(w, `{ + "name":"%s", + "deploy_access_levels": [ + { + "id": 42, + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ], + "required_approval_count": 0, + "approval_rules": [] + }`, environmentName) + }) + + expected = &GroupProtectedEnvironment{ + Name: environmentName, + DeployAccessLevels: []*GroupEnvironmentAccessDescription{ + { + ID: 42, + AccessLevel: 30, + AccessLevelDescription: "Developers + Maintainers", + }, + }, + RequiredApprovalCount: 0, + ApprovalRules: []*GroupEnvironmentApprovalRule{}, + } + + opt = &UpdateGroupProtectedEnvironmentOptions{ + Name: String(environmentName), + ApprovalRules: &[]*UpdateGroupEnvironmentApprovalRuleOptions{ + { + ID: Int(1), + Destroy: Bool(true), + }, + }, + RequiredApprovalCount: Int(0), + } + + environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(5, environmentName, opt) + assert.NoError(t, err, "failed to get response") + assert.Equal(t, expected, environment) +} + +func TestGroupUnprotectEnvironments(t *testing.T) { + mux, client := setup(t) + + mux.HandleFunc("/api/v4/groups/1/protected_environments/staging", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodDelete) + }) + + resp, err := client.GroupProtectedEnvironments.UnprotectGroupEnvironment(1, "staging") + assert.NoError(t, err, "failed to get response") + assert.Equal(t, http.StatusOK, resp.StatusCode) +}