提交 26b7b475 编写于 作者: M Mathieu Parent

Add *_access_level to project API

- issues_access_level
- repository_access_level
- merge_requests_access_level
- builds_access_level
- wiki_access_level
- snippets_access_level
上级 406b67ca
...@@ -9,32 +9,70 @@ require 'gitlab/utils' ...@@ -9,32 +9,70 @@ require 'gitlab/utils'
module ProjectFeaturesCompatibility module ProjectFeaturesCompatibility
extend ActiveSupport::Concern extend ActiveSupport::Concern
# TODO: remove in API v5, replaced by *_access_level
def wiki_enabled=(value) def wiki_enabled=(value)
write_feature_attribute(:wiki_access_level, value) write_feature_attribute_boolean(:wiki_access_level, value)
end end
# TODO: remove in API v5, replaced by *_access_level
def builds_enabled=(value) def builds_enabled=(value)
write_feature_attribute(:builds_access_level, value) write_feature_attribute_boolean(:builds_access_level, value)
end end
# TODO: remove in API v5, replaced by *_access_level
def merge_requests_enabled=(value) def merge_requests_enabled=(value)
write_feature_attribute(:merge_requests_access_level, value) write_feature_attribute_boolean(:merge_requests_access_level, value)
end end
# TODO: remove in API v5, replaced by *_access_level
def issues_enabled=(value) def issues_enabled=(value)
write_feature_attribute(:issues_access_level, value) write_feature_attribute_boolean(:issues_access_level, value)
end end
# TODO: remove in API v5, replaced by *_access_level
def snippets_enabled=(value) def snippets_enabled=(value)
write_feature_attribute(:snippets_access_level, value) write_feature_attribute_boolean(:snippets_access_level, value)
end
def repository_access_level=(value)
write_feature_attribute_string(:repository_access_level, value)
end
def wiki_access_level=(value)
write_feature_attribute_string(:wiki_access_level, value)
end
def builds_access_level=(value)
write_feature_attribute_string(:builds_access_level, value)
end
def merge_requests_access_level=(value)
write_feature_attribute_string(:merge_requests_access_level, value)
end
def issues_access_level=(value)
write_feature_attribute_string(:issues_access_level, value)
end
def snippets_access_level=(value)
write_feature_attribute_string(:snippets_access_level, value)
end end
private private
def write_feature_attribute(field, value) def write_feature_attribute_boolean(field, value)
access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
write_feature_attribute_raw(field, access_level)
end
def write_feature_attribute_string(field, value)
access_level = ProjectFeature.access_level_from_str(value)
write_feature_attribute_raw(field, access_level)
end
def write_feature_attribute_raw(field, value)
build_project_feature unless project_feature build_project_feature unless project_feature
access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend
project_feature.__send__(:write_attribute, field, access_level) # rubocop:disable GitlabSecurity/PublicSend
end end
end end
...@@ -24,6 +24,12 @@ class ProjectFeature < ApplicationRecord ...@@ -24,6 +24,12 @@ class ProjectFeature < ApplicationRecord
FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER }.freeze PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER }.freeze
STRING_OPTIONS = HashWithIndifferentAccess.new({
'disabled' => DISABLED,
'private' => PRIVATE,
'enabled' => ENABLED,
'public' => PUBLIC
}).freeze
class << self class << self
def access_level_attribute(feature) def access_level_attribute(feature)
...@@ -45,6 +51,14 @@ class ProjectFeature < ApplicationRecord ...@@ -45,6 +51,14 @@ class ProjectFeature < ApplicationRecord
PRIVATE_FEATURES_MIN_ACCESS_LEVEL.fetch(feature, Gitlab::Access::GUEST) PRIVATE_FEATURES_MIN_ACCESS_LEVEL.fetch(feature, Gitlab::Access::GUEST)
end end
def access_level_from_str(level)
STRING_OPTIONS.fetch(level)
end
def str_from_access_level(level)
STRING_OPTIONS.key(level)
end
private private
def ensure_feature!(feature) def ensure_feature!(feature)
...@@ -83,6 +97,10 @@ class ProjectFeature < ApplicationRecord ...@@ -83,6 +97,10 @@ class ProjectFeature < ApplicationRecord
public_send(ProjectFeature.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend public_send(ProjectFeature.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend
end end
def string_access_level(feature)
ProjectFeature.str_from_access_level(access_level(feature))
end
def builds_enabled? def builds_enabled?
builds_access_level > DISABLED builds_access_level > DISABLED
end end
......
...@@ -708,11 +708,17 @@ POST /projects ...@@ -708,11 +708,17 @@ POST /projects
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) | | `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
| `default_branch` | string | no | `master` by default | | `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description | | `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project | | `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project | | `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead |
| `jobs_enabled` | boolean | no | Enable jobs for this project | | `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead |
| `wiki_enabled` | boolean | no | Enable wiki for this project | | `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead |
| `snippets_enabled` | boolean | no | Enable snippets for this project | | `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead |
| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project | | `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project | | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
...@@ -753,11 +759,17 @@ POST /projects/user/:user_id ...@@ -753,11 +759,17 @@ POST /projects/user/:user_id
| `path` | string | no | Custom repository name for new project. By default generated based on name | | `path` | string | no | Custom repository name for new project. By default generated based on name |
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) | | `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
| `description` | string | no | Short project description | | `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project | | `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project | | `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead |
| `jobs_enabled` | boolean | no | Enable jobs for this project | | `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead |
| `wiki_enabled` | boolean | no | Enable wiki for this project | | `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead |
| `snippets_enabled` | boolean | no | Enable snippets for this project | | `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead |
| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project | | `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project | | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
...@@ -798,11 +810,17 @@ PUT /projects/:id ...@@ -798,11 +810,17 @@ PUT /projects/:id
| `path` | string | no | Custom repository name for the project. By default generated based on name | | `path` | string | no | Custom repository name for the project. By default generated based on name |
| `default_branch` | string | no | `master` by default | | `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description | | `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project | | `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project | | `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead |
| `jobs_enabled` | boolean | no | Enable jobs for this project | | `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead |
| `wiki_enabled` | boolean | no | Enable wiki for this project | | `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead |
| `snippets_enabled` | boolean | no | Enable snippets for this project | | `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead |
| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project | | `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project | | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
......
...@@ -247,12 +247,20 @@ module API ...@@ -247,12 +247,20 @@ module API
expose :container_registry_enabled expose :container_registry_enabled
# Expose old field names with the new permissions methods to keep API compatible # Expose old field names with the new permissions methods to keep API compatible
# TODO: remove in API v5, replaced by *_access_level
expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) } expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) } expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
expose(:issues_access_level) { |project, options| project.project_feature.string_access_level(:issues) }
expose(:repository_access_level) { |project, options| project.project_feature.string_access_level(:repository) }
expose(:merge_requests_access_level) { |project, options| project.project_feature.string_access_level(:merge_requests) }
expose(:wiki_access_level) { |project, options| project.project_feature.string_access_level(:wiki) }
expose(:builds_access_level) { |project, options| project.project_feature.string_access_level(:builds) }
expose(:snippets_access_level) { |project, options| project.project_feature.string_access_level(:snippets) }
expose :shared_runners_enabled expose :shared_runners_enabled
expose :lfs_enabled?, as: :lfs_enabled expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id expose :creator_id
......
...@@ -9,11 +9,21 @@ module API ...@@ -9,11 +9,21 @@ module API
params :optional_project_params_ce do params :optional_project_params_ce do
optional :description, type: String, desc: 'The description of the project' optional :description, type: String, desc: 'The description of the project'
optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`' optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`'
# TODO: remove in API v5, replaced by *_access_level
optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled' optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled' optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled' optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled'
optional :jobs_enabled, type: Boolean, desc: 'Flag indication if jobs are enabled' optional :jobs_enabled, type: Boolean, desc: 'Flag indication if jobs are enabled'
optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled' optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled'
optional :issues_access_level, type: String, values: %w(disabled private enabled), desc: 'Issues access level. One of `disabled`, `private` or `enabled`'
optional :repository_access_level, type: String, values: %w(disabled private enabled), desc: 'Repository access level. One of `disabled`, `private` or `enabled`'
optional :merge_requests_access_level, type: String, values: %w(disabled private enabled), desc: 'Merge requests access level. One of `disabled`, `private` or `enabled`'
optional :wiki_access_level, type: String, values: %w(disabled private enabled), desc: 'Wiki access level. One of `disabled`, `private` or `enabled`'
optional :builds_access_level, type: String, values: %w(disabled private enabled), desc: 'Builds access level. One of `disabled`, `private` or `enabled`'
optional :snippets_access_level, type: String, values: %w(disabled private enabled), desc: 'Snippets access level. One of `disabled`, `private` or `enabled`'
optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push' optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push'
optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project' optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
...@@ -48,15 +58,14 @@ module API ...@@ -48,15 +58,14 @@ module API
def self.update_params_at_least_one_of def self.update_params_at_least_one_of
[ [
:jobs_enabled, :builds_access_level,
:resolve_outdated_diff_discussions,
:ci_config_path, :ci_config_path,
:container_registry_enabled, :container_registry_enabled,
:default_branch, :default_branch,
:description, :description,
:issues_enabled, :issues_access_level,
:lfs_enabled, :lfs_enabled,
:merge_requests_enabled, :merge_requests_access_level,
:merge_method, :merge_method,
:name, :name,
:only_allow_merge_if_all_discussions_are_resolved, :only_allow_merge_if_all_discussions_are_resolved,
...@@ -64,14 +73,24 @@ module API ...@@ -64,14 +73,24 @@ module API
:path, :path,
:printing_merge_request_link_enabled, :printing_merge_request_link_enabled,
:public_builds, :public_builds,
:repository_access_level,
:request_access_enabled, :request_access_enabled,
:resolve_outdated_diff_discussions,
:shared_runners_enabled, :shared_runners_enabled,
:snippets_enabled, :snippets_access_level,
:tag_list, :tag_list,
:visibility, :visibility,
:wiki_enabled, :wiki_access_level,
:avatar, :avatar,
:external_authorization_classification_label :external_authorization_classification_label,
# TODO: remove in API v5, replaced by *_access_level
:issues_enabled,
:jobs_enabled,
:merge_requests_enabled,
:wiki_enabled,
:jobs_enabled,
:snippets_enabled
] ]
end end
end end
......
...@@ -4,7 +4,8 @@ require 'spec_helper' ...@@ -4,7 +4,8 @@ require 'spec_helper'
describe ProjectFeaturesCompatibility do describe ProjectFeaturesCompatibility do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:features) { %w(issues wiki builds merge_requests snippets) } let(:features_except_repository) { %w(issues wiki builds merge_requests snippets) }
let(:features) { features_except_repository + ['repository'] }
# We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table # We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table
# All those fields got moved to a new table called project_feature and are now integers instead of booleans # All those fields got moved to a new table called project_feature and are now integers instead of booleans
...@@ -12,30 +13,37 @@ describe ProjectFeaturesCompatibility do ...@@ -12,30 +13,37 @@ describe ProjectFeaturesCompatibility do
# So we can keep it compatible # So we can keep it compatible
it "converts fields from 'true' to ProjectFeature::ENABLED" do it "converts fields from 'true' to ProjectFeature::ENABLED" do
features.each do |feature| features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, "true") project.update_attribute("#{feature}_enabled".to_sym, "true")
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED) expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED)
end end
end end
it "converts fields from 'false' to ProjectFeature::DISABLED" do it "converts fields from 'false' to ProjectFeature::DISABLED" do
features.each do |feature| features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, "false") project.update_attribute("#{feature}_enabled".to_sym, "false")
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED) expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED)
end end
end end
it "converts fields from true to ProjectFeature::ENABLED" do it "converts fields from true to ProjectFeature::ENABLED" do
features.each do |feature| features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, true) project.update_attribute("#{feature}_enabled".to_sym, true)
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED) expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED)
end end
end end
it "converts fields from false to ProjectFeature::DISABLED" do it "converts fields from false to ProjectFeature::DISABLED" do
features.each do |feature| features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, false) project.update_attribute("#{feature}_enabled".to_sym, false)
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED) expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED)
end end
end end
it "accepts private as ProjectFeature::PRIVATE" do
features.each do |feature|
project.update!("#{feature}_access_level".to_sym => 'private')
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::PRIVATE)
end
end
end end
...@@ -1102,6 +1102,12 @@ describe API::Projects do ...@@ -1102,6 +1102,12 @@ describe API::Projects do
expect(json_response['wiki_enabled']).to be_present expect(json_response['wiki_enabled']).to be_present
expect(json_response['jobs_enabled']).to be_present expect(json_response['jobs_enabled']).to be_present
expect(json_response['snippets_enabled']).to be_present expect(json_response['snippets_enabled']).to be_present
expect(json_response['snippets_access_level']).to be_present
expect(json_response['repository_access_level']).to be_present
expect(json_response['issues_access_level']).to be_present
expect(json_response['merge_requests_access_level']).to be_present
expect(json_response['wiki_access_level']).to be_present
expect(json_response['builds_access_level']).to be_present
expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions) expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions)
expect(json_response['container_registry_enabled']).to be_present expect(json_response['container_registry_enabled']).to be_present
expect(json_response['created_at']).to be_present expect(json_response['created_at']).to be_present
...@@ -1913,6 +1919,16 @@ describe API::Projects do ...@@ -1913,6 +1919,16 @@ describe API::Projects do
end end
end end
it 'updates builds_access_level' do
project_param = { builds_access_level: 'private' }
put api("/projects/#{project3.id}", user), params: project_param
expect(response).to have_gitlab_http_status(200)
expect(json_response['builds_access_level']).to eq('private')
end
it 'updates merge_method' do it 'updates merge_method' do
project_param = { merge_method: 'ff' } project_param = { merge_method: 'ff' }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册