Merge branch 'ericy_ts/protected_branches_api' into 'master'
Extending API for protected branches See merge request !12756
This commit is contained in:
commit
92bfd5ba3e
11 changed files with 504 additions and 8 deletions
|
@ -1,6 +1,12 @@
|
||||||
module ProtectedBranchAccess
|
module ProtectedBranchAccess
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
ALLOWED_ACCESS_LEVELS ||= [
|
||||||
|
Gitlab::Access::MASTER,
|
||||||
|
Gitlab::Access::DEVELOPER,
|
||||||
|
Gitlab::Access::NO_ACCESS
|
||||||
|
].freeze
|
||||||
|
|
||||||
included do
|
included do
|
||||||
include ProtectedRefAccess
|
include ProtectedRefAccess
|
||||||
|
|
||||||
|
@ -9,11 +15,7 @@ module ProtectedBranchAccess
|
||||||
delegate :project, to: :protected_branch
|
delegate :project, to: :protected_branch
|
||||||
|
|
||||||
validates :access_level, presence: true, inclusion: {
|
validates :access_level, presence: true, inclusion: {
|
||||||
in: [
|
in: ALLOWED_ACCESS_LEVELS
|
||||||
Gitlab::Access::MASTER,
|
|
||||||
Gitlab::Access::DEVELOPER,
|
|
||||||
Gitlab::Access::NO_ACCESS
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def self.human_access_levels
|
def self.human_access_levels
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add API for protected branches to allow for wildcard matching and no access
|
||||||
|
restrictions
|
||||||
|
merge_request: 12756
|
||||||
|
author: Eric Yu
|
|
@ -43,6 +43,7 @@ following locations:
|
||||||
- [Project Access Requests](access_requests.md)
|
- [Project Access Requests](access_requests.md)
|
||||||
- [Project Members](members.md)
|
- [Project Members](members.md)
|
||||||
- [Project Snippets](project_snippets.md)
|
- [Project Snippets](project_snippets.md)
|
||||||
|
- [Protected Branches](protected_branches.md)
|
||||||
- [Repositories](repositories.md)
|
- [Repositories](repositories.md)
|
||||||
- [Repository Files](repository_files.md)
|
- [Repository Files](repository_files.md)
|
||||||
- [Runners](runners.md)
|
- [Runners](runners.md)
|
||||||
|
|
|
@ -95,6 +95,8 @@ Example response:
|
||||||
|
|
||||||
## Protect repository branch
|
## Protect repository branch
|
||||||
|
|
||||||
|
>**Note:** This API endpoint is deprecated in favor of `POST /projects/:id/protected_branches`.
|
||||||
|
|
||||||
Protects a single project repository branch. This is an idempotent function,
|
Protects a single project repository branch. This is an idempotent function,
|
||||||
protecting an already protected repository branch still returns a `200 OK`
|
protecting an already protected repository branch still returns a `200 OK`
|
||||||
status code.
|
status code.
|
||||||
|
@ -143,6 +145,8 @@ Example response:
|
||||||
|
|
||||||
## Unprotect repository branch
|
## Unprotect repository branch
|
||||||
|
|
||||||
|
>**Note:** This API endpoint is deprecated in favor of `DELETE /projects/:id/protected_branches/:name`
|
||||||
|
|
||||||
Unprotects a single project repository branch. This is an idempotent function,
|
Unprotects a single project repository branch. This is an idempotent function,
|
||||||
unprotecting an already unprotected repository branch still returns a `200 OK`
|
unprotecting an already unprotected repository branch still returns a `200 OK`
|
||||||
status code.
|
status code.
|
||||||
|
|
145
doc/api/protected_branches.md
Normal file
145
doc/api/protected_branches.md
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
# Protected branches API
|
||||||
|
|
||||||
|
>**Note:** This feature was introduced in GitLab 9.5
|
||||||
|
|
||||||
|
**Valid access levels**
|
||||||
|
|
||||||
|
The access levels are defined in the `ProtectedBranchAccess::ALLOWED_ACCESS_LEVELS` constant. Currently, these levels are recognized:
|
||||||
|
```
|
||||||
|
0 => No access
|
||||||
|
30 => Developer access
|
||||||
|
40 => Master access
|
||||||
|
```
|
||||||
|
|
||||||
|
## List protected branches
|
||||||
|
|
||||||
|
Gets a list of protected branches from a project.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /projects/:id/protected_branches
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ---- | -------- | ----------- |
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_branches'
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "master",
|
||||||
|
"push_access_levels": [
|
||||||
|
{
|
||||||
|
"access_level": 40,
|
||||||
|
"access_level_description": "Masters"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"merge_access_levels": [
|
||||||
|
{
|
||||||
|
"access_level": 40,
|
||||||
|
"access_level_description": "Masters"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get a single protected branch or wildcard protected branch
|
||||||
|
|
||||||
|
Gets a single protected branch or wildcard protected branch.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /projects/:id/protected_branches/:name
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ---- | -------- | ----------- |
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `name` | string | yes | The name of the branch or wildcard |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_branches/master'
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "master",
|
||||||
|
"push_access_levels": [
|
||||||
|
{
|
||||||
|
"access_level": 40,
|
||||||
|
"access_level_description": "Masters"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"merge_access_levels": [
|
||||||
|
{
|
||||||
|
"access_level": 40,
|
||||||
|
"access_level_description": "Masters"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protect repository branches
|
||||||
|
|
||||||
|
Protects a single repository branch or several project repository
|
||||||
|
branches using a wildcard protected branch.
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /projects/:id/protected_branches
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_branches?name=*-stable&push_access_level=30&merge_access_level=30'
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ---- | -------- | ----------- |
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `name` | string | yes | The name of the branch or wildcard |
|
||||||
|
| `push_access_level` | string | no | Access levels allowed to push (defaults: `40`, master access level) |
|
||||||
|
| `merge_access_level` | string | no | Access levels allowed to merge (defaults: `40`, master access level) |
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "*-stable",
|
||||||
|
"push_access_levels": [
|
||||||
|
{
|
||||||
|
"access_level": 30,
|
||||||
|
"access_level_description": "Developers + Masters"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"merge_access_levels": [
|
||||||
|
{
|
||||||
|
"access_level": 30,
|
||||||
|
"access_level_description": "Developers + Masters"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unprotect repository branches
|
||||||
|
|
||||||
|
Unprotects the given protected branch or wildcard protected branch.
|
||||||
|
|
||||||
|
```
|
||||||
|
DELETE /projects/:id/protected_branches/:name
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_branches/*-stable'
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ---- | -------- | ----------- |
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `name` | string | yes | The name of the branch |
|
|
@ -123,6 +123,7 @@ module API
|
||||||
mount ::API::ProjectHooks
|
mount ::API::ProjectHooks
|
||||||
mount ::API::Projects
|
mount ::API::Projects
|
||||||
mount ::API::ProjectSnippets
|
mount ::API::ProjectSnippets
|
||||||
|
mount ::API::ProtectedBranches
|
||||||
mount ::API::Repositories
|
mount ::API::Repositories
|
||||||
mount ::API::Runner
|
mount ::API::Runner
|
||||||
mount ::API::Runners
|
mount ::API::Runners
|
||||||
|
|
|
@ -37,6 +37,7 @@ module API
|
||||||
present branch, with: Entities::RepoBranch, project: user_project
|
present branch, with: Entities::RepoBranch, project: user_project
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Note: This API will be deprecated in favor of the protected branches API.
|
||||||
# Note: The internal data model moved from `developers_can_{merge,push}` to `allowed_to_{merge,push}`
|
# Note: The internal data model moved from `developers_can_{merge,push}` to `allowed_to_{merge,push}`
|
||||||
# in `gitlab-org/gitlab-ce!5081`. The API interface has not been changed (to maintain compatibility),
|
# in `gitlab-org/gitlab-ce!5081`. The API interface has not been changed (to maintain compatibility),
|
||||||
# but it works with the changed data model to infer `developers_can_merge` and `developers_can_push`.
|
# but it works with the changed data model to infer `developers_can_merge` and `developers_can_push`.
|
||||||
|
@ -65,9 +66,9 @@ module API
|
||||||
service_args = [user_project, current_user, protected_branch_params]
|
service_args = [user_project, current_user, protected_branch_params]
|
||||||
|
|
||||||
protected_branch = if protected_branch
|
protected_branch = if protected_branch
|
||||||
ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch)
|
::ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch)
|
||||||
else
|
else
|
||||||
ProtectedBranches::ApiCreateService.new(*service_args).execute
|
::ProtectedBranches::ApiCreateService.new(*service_args).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
if protected_branch.valid?
|
if protected_branch.valid?
|
||||||
|
@ -77,6 +78,7 @@ module API
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Note: This API will be deprecated in favor of the protected branches API.
|
||||||
desc 'Unprotect a single branch' do
|
desc 'Unprotect a single branch' do
|
||||||
success Entities::RepoBranch
|
success Entities::RepoBranch
|
||||||
end
|
end
|
||||||
|
|
|
@ -240,7 +240,7 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :protected do |repo_branch, options|
|
expose :protected do |repo_branch, options|
|
||||||
ProtectedBranch.protected?(options[:project], repo_branch.name)
|
::ProtectedBranch.protected?(options[:project], repo_branch.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :developers_can_push do |repo_branch, options|
|
expose :developers_can_push do |repo_branch, options|
|
||||||
|
@ -299,6 +299,19 @@ module API
|
||||||
expose :deleted_file?, as: :deleted_file
|
expose :deleted_file?, as: :deleted_file
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ProtectedRefAccess < Grape::Entity
|
||||||
|
expose :access_level
|
||||||
|
expose :access_level_description do |protected_ref_access|
|
||||||
|
protected_ref_access.humanize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ProtectedBranch < Grape::Entity
|
||||||
|
expose :name
|
||||||
|
expose :push_access_levels, using: Entities::ProtectedRefAccess
|
||||||
|
expose :merge_access_levels, using: Entities::ProtectedRefAccess
|
||||||
|
end
|
||||||
|
|
||||||
class Milestone < Grape::Entity
|
class Milestone < Grape::Entity
|
||||||
expose :id, :iid
|
expose :id, :iid
|
||||||
expose :project_id, if: -> (entity, options) { entity&.project_id }
|
expose :project_id, if: -> (entity, options) { entity&.project_id }
|
||||||
|
|
85
lib/api/protected_branches.rb
Normal file
85
lib/api/protected_branches.rb
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
module API
|
||||||
|
class ProtectedBranches < Grape::API
|
||||||
|
include PaginationParams
|
||||||
|
|
||||||
|
BRANCH_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)
|
||||||
|
|
||||||
|
before { authorize_admin_project }
|
||||||
|
|
||||||
|
params do
|
||||||
|
requires :id, type: String, desc: 'The ID of a project'
|
||||||
|
end
|
||||||
|
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
|
||||||
|
desc "Get a project's protected branches" do
|
||||||
|
success Entities::ProtectedBranch
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
use :pagination
|
||||||
|
end
|
||||||
|
get ':id/protected_branches' do
|
||||||
|
protected_branches = user_project.protected_branches.preload(:push_access_levels, :merge_access_levels)
|
||||||
|
|
||||||
|
present paginate(protected_branches), with: Entities::ProtectedBranch, project: user_project
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Get a single protected branch' do
|
||||||
|
success Entities::ProtectedBranch
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :name, type: String, desc: 'The name of the branch or wildcard'
|
||||||
|
end
|
||||||
|
get ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
|
||||||
|
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
|
||||||
|
|
||||||
|
present protected_branch, with: Entities::ProtectedBranch, project: user_project
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Protect a single branch or wildcard' do
|
||||||
|
success Entities::ProtectedBranch
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :name, type: String, desc: 'The name of the protected branch'
|
||||||
|
optional :push_access_level, type: Integer, default: Gitlab::Access::MASTER,
|
||||||
|
values: ProtectedBranchAccess::ALLOWED_ACCESS_LEVELS,
|
||||||
|
desc: 'Access levels allowed to push (defaults: `40`, master access level)'
|
||||||
|
optional :merge_access_level, type: Integer, default: Gitlab::Access::MASTER,
|
||||||
|
values: ProtectedBranchAccess::ALLOWED_ACCESS_LEVELS,
|
||||||
|
desc: 'Access levels allowed to merge (defaults: `40`, master access level)'
|
||||||
|
end
|
||||||
|
post ':id/protected_branches' do
|
||||||
|
protected_branch = user_project.protected_branches.find_by(name: params[:name])
|
||||||
|
if protected_branch
|
||||||
|
conflict!("Protected branch '#{params[:name]}' already exists")
|
||||||
|
end
|
||||||
|
|
||||||
|
protected_branch_params = {
|
||||||
|
name: params[:name],
|
||||||
|
push_access_levels_attributes: [{ access_level: params[:push_access_level] }],
|
||||||
|
merge_access_levels_attributes: [{ access_level: params[:merge_access_level] }]
|
||||||
|
}
|
||||||
|
|
||||||
|
service_args = [user_project, current_user, protected_branch_params]
|
||||||
|
|
||||||
|
protected_branch = ::ProtectedBranches::CreateService.new(*service_args).execute
|
||||||
|
|
||||||
|
if protected_branch.persisted?
|
||||||
|
present protected_branch, with: Entities::ProtectedBranch, project: user_project
|
||||||
|
else
|
||||||
|
render_api_error!(protected_branch.errors.full_messages, 422)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Unprotect a single branch'
|
||||||
|
params do
|
||||||
|
requires :name, type: String, desc: 'The name of the protected branch'
|
||||||
|
end
|
||||||
|
delete ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
|
||||||
|
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
|
||||||
|
|
||||||
|
protected_branch.destroy
|
||||||
|
|
||||||
|
status 204
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -57,5 +57,11 @@ FactoryGirl.define do
|
||||||
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER)
|
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trait :no_one_can_merge do
|
||||||
|
after(:create) do |protected_branch|
|
||||||
|
protected_branch.merge_access_levels.first.update!(access_level: Gitlab::Access::NO_ACCESS)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
232
spec/requests/api/protected_branches_spec.rb
Normal file
232
spec/requests/api/protected_branches_spec.rb
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe API::ProtectedBranches do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let!(:project) { create(:project, :repository) }
|
||||||
|
let(:protected_name) { 'feature' }
|
||||||
|
let(:branch_name) { protected_name }
|
||||||
|
let!(:protected_branch) do
|
||||||
|
create(:protected_branch, project: project, name: protected_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /projects/:id/protected_branches" do
|
||||||
|
let(:route) { "/projects/#{project.id}/protected_branches" }
|
||||||
|
|
||||||
|
shared_examples_for 'protected branches' do
|
||||||
|
it 'returns the protected branches' do
|
||||||
|
get api(route, user), per_page: 100
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(200)
|
||||||
|
expect(response).to include_pagination_headers
|
||||||
|
expect(json_response).to be_an Array
|
||||||
|
|
||||||
|
protected_branch_names = json_response.map { |x| x['name'] }
|
||||||
|
expected_branch_names = project.protected_branches.map { |x| x['name'] }
|
||||||
|
expect(protected_branch_names).to match_array(expected_branch_names)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when authenticated as a master' do
|
||||||
|
before do
|
||||||
|
project.add_master(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'protected branches'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when authenticated as a guest' do
|
||||||
|
before do
|
||||||
|
project.add_guest(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like '403 response' do
|
||||||
|
let(:request) { get api(route, user) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /projects/:id/protected_branches/:branch" do
|
||||||
|
let(:route) { "/projects/#{project.id}/protected_branches/#{branch_name}" }
|
||||||
|
|
||||||
|
shared_examples_for 'protected branch' do
|
||||||
|
it 'returns the protected branch' do
|
||||||
|
get api(route, user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(200)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when protected branch does not exist' do
|
||||||
|
let(:branch_name) { 'unknown' }
|
||||||
|
|
||||||
|
it_behaves_like '404 response' do
|
||||||
|
let(:request) { get api(route, user) }
|
||||||
|
let(:message) { '404 Not found' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when authenticated as a master' do
|
||||||
|
before do
|
||||||
|
project.add_master(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'protected branch'
|
||||||
|
|
||||||
|
context 'when protected branch contains a wildcard' do
|
||||||
|
let(:protected_name) { 'feature*' }
|
||||||
|
|
||||||
|
it_behaves_like 'protected branch'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when authenticated as a guest' do
|
||||||
|
before do
|
||||||
|
project.add_guest(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like '403 response' do
|
||||||
|
let(:request) { get api(route, user) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /projects/:id/protected_branches' do
|
||||||
|
let(:branch_name) { 'new_branch' }
|
||||||
|
|
||||||
|
context 'when authenticated as a master' do
|
||||||
|
before do
|
||||||
|
project.add_master(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects a single branch' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user), name: branch_name
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects a single branch and developers can push' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user),
|
||||||
|
name: branch_name, push_access_level: 30
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects a single branch and developers can merge' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user),
|
||||||
|
name: branch_name, merge_access_level: 30
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects a single branch and developers can push and merge' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user),
|
||||||
|
name: branch_name, push_access_level: 30, merge_access_level: 30
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects a single branch and no one can push' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user),
|
||||||
|
name: branch_name, push_access_level: 0
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects a single branch and no one can merge' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user),
|
||||||
|
name: branch_name, merge_access_level: 0
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects a single branch and no one can push or merge' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user),
|
||||||
|
name: branch_name, push_access_level: 0, merge_access_level: 0
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a 409 error if the same branch is protected twice' do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user), name: protected_name
|
||||||
|
expect(response).to have_gitlab_http_status(409)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when branch has a wildcard in its name' do
|
||||||
|
let(:branch_name) { 'feature/*' }
|
||||||
|
|
||||||
|
it "protects multiple branches with a wildcard in the name" do
|
||||||
|
post api("/projects/#{project.id}/protected_branches", user), name: branch_name
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['name']).to eq(branch_name)
|
||||||
|
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when authenticated as a guest' do
|
||||||
|
before do
|
||||||
|
project.add_guest(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a 403 error if guest" do
|
||||||
|
post api("/projects/#{project.id}/protected_branches/", user), name: branch_name
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /projects/:id/protected_branches/unprotect/:branch" do
|
||||||
|
before do
|
||||||
|
project.add_master(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "unprotects a single branch" do
|
||||||
|
delete api("/projects/#{project.id}/protected_branches/#{branch_name}", user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(204)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 if branch does not exist" do
|
||||||
|
delete api("/projects/#{project.id}/protected_branches/barfoo", user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(404)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when branch has a wildcard in its name' do
|
||||||
|
let(:protected_name) { 'feature*' }
|
||||||
|
|
||||||
|
it "unprotects a wildcard branch" do
|
||||||
|
delete api("/projects/#{project.id}/protected_branches/#{branch_name}", user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(204)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue