Commit 109956a6 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'project_api' into 'master'

Improve Project API

Closes #49938, #40892, and #35992

See merge request gitlab-org/gitlab-ce!28327
parents 6e106f66 2615265e
# frozen_string_literal: true
# Add methods used by the projects API
module ProjectAPICompatibility
extend ActiveSupport::Concern
def build_git_strategy=(value)
write_attribute(:build_allow_git_fetch, value == 'fetch')
end
def auto_devops_enabled=(value)
self.build_auto_devops if self.auto_devops&.enabled.nil?
self.auto_devops.update! enabled: value
end
def auto_devops_deploy_strategy=(value)
self.build_auto_devops if self.auto_devops&.enabled.nil?
self.auto_devops.update! deploy_strategy: value
end
end
...@@ -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
...@@ -15,6 +15,7 @@ class Project < ApplicationRecord ...@@ -15,6 +15,7 @@ class Project < ApplicationRecord
include CaseSensitivity include CaseSensitivity
include TokenAuthenticatable include TokenAuthenticatable
include ValidAttribute include ValidAttribute
include ProjectAPICompatibility
include ProjectFeaturesCompatibility include ProjectFeaturesCompatibility
include SelectForProjectAuthorization include SelectForProjectAuthorization
include Presentable include Presentable
......
...@@ -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
......
---
title: Improve Project API
merge_request: 28327
author: Mathieu Parent
type: added
...@@ -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 |
...@@ -727,7 +733,13 @@ POST /projects ...@@ -727,7 +733,13 @@ POST /projects
| `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project | | `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project |
| `avatar` | mixed | no | Image file for avatar of the project | | `avatar` | mixed | no | Image file for avatar of the project |
| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line | | `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` |
| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) |
| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled |
| `build_coverage_regex` | string | no | Test coverage parsing |
| `ci_config_path` | string | no | The path to CI config file | | `ci_config_path` | string | no | The path to CI config file |
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project |
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins | | `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default | | `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default |
| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project | | `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project |
...@@ -753,11 +765,17 @@ POST /projects/user/:user_id ...@@ -753,11 +765,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 |
...@@ -772,7 +790,13 @@ POST /projects/user/:user_id ...@@ -772,7 +790,13 @@ POST /projects/user/:user_id
| `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project | | `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project |
| `avatar` | mixed | no | Image file for avatar of the project | | `avatar` | mixed | no | Image file for avatar of the project |
| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line | | `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` |
| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) |
| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled |
| `build_coverage_regex` | string | no | Test coverage parsing |
| `ci_config_path` | string | no | The path to CI config file | | `ci_config_path` | string | no | The path to CI config file |
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project |
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins | | `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default | | `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default |
| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project | | `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project |
...@@ -798,11 +822,17 @@ PUT /projects/:id ...@@ -798,11 +822,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 |
...@@ -816,8 +846,14 @@ PUT /projects/:id ...@@ -816,8 +846,14 @@ PUT /projects/:id
| `request_access_enabled` | boolean | no | Allow users to request member access | | `request_access_enabled` | boolean | no | Allow users to request member access |
| `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project | | `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project |
| `avatar` | mixed | no | Image file for avatar of the project | | `avatar` | mixed | no | Image file for avatar of the project |
| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` |
| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) |
| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled |
| `build_coverage_regex` | string | no | Test coverage parsing |
| `ci_config_path` | string | no | The path to CI config file | | `ci_config_path` | string | no | The path to CI config file |
| `ci_default_git_depth` | integer | no | Default number of revisions for [shallow cloning](../user/project/pipelines/settings.md#git-shallow-clone) | | `ci_default_git_depth` | integer | no | Default number of revisions for [shallow cloning](../user/project/pipelines/settings.md#git-shallow-clone) |
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project |
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins | | `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge request by default | | `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge request by default |
| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project | | `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project |
......
...@@ -201,6 +201,7 @@ module API ...@@ -201,6 +201,7 @@ module API
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555 # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
projects_relation.preload(:project_feature, :route) projects_relation.preload(:project_feature, :route)
.preload(:import_state, :tags) .preload(:import_state, :tags)
.preload(:auto_devops)
.preload(namespace: [:route, :owner]) .preload(namespace: [:route, :owner])
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -247,12 +248,20 @@ module API ...@@ -247,12 +248,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
...@@ -267,6 +276,12 @@ module API ...@@ -267,6 +276,12 @@ module API
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :ci_default_git_depth expose :ci_default_git_depth
expose :public_builds, as: :public_jobs expose :public_builds, as: :public_jobs
expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
project.build_allow_git_fetch ? 'fetch' : 'clone'
end
expose :build_timeout
expose :auto_cancel_pending_pipelines
expose :build_coverage_regex
expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) } expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
expose :shared_with_groups do |project, options| expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links, options) SharedGroup.represent(project.project_group_links, options)
...@@ -280,6 +295,10 @@ module API ...@@ -280,6 +295,10 @@ module API
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project) options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
} }
expose :external_authorization_classification_label expose :external_authorization_classification_label
expose :auto_devops_enabled?, as: :auto_devops_enabled
expose :auto_devops_deploy_strategy do |project, options|
project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {}) def self.preload_relation(projects_relation, options = {})
...@@ -289,6 +308,7 @@ module API ...@@ -289,6 +308,7 @@ module API
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555 # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
super(projects_relation).preload(:group) super(projects_relation).preload(:group)
.preload(:ci_cd_settings) .preload(:ci_cd_settings)
.preload(:auto_devops)
.preload(project_group_links: { group: :route }, .preload(project_group_links: { group: :route },
fork_network: :root_project, fork_network: :root_project,
fork_network_member: :forked_from_project, fork_network_member: :forked_from_project,
......
...@@ -8,12 +8,26 @@ module API ...@@ -8,12 +8,26 @@ 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 :build_git_strategy, type: String, values: %w(fetch clone), desc: 'The Git strategy. Defaults to `fetch`'
optional :build_timeout, type: Integer, desc: 'Build timeout'
optional :auto_cancel_pending_pipelines, type: String, values: %w(disabled enabled), desc: 'Auto-cancel pending pipelines'
optional :build_coverage_regex, type: String, desc: 'Test coverage parsing'
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'
...@@ -30,6 +44,8 @@ module API ...@@ -30,6 +44,8 @@ module API
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md" optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project' optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project'
optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning' optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled'
optional :auto_devops_deploy_strategy, type: String, values: %w(continuous manual timed_incremental), desc: 'Auto Deploy strategy'
end end
params :optional_project_params_ee do params :optional_project_params_ee do
...@@ -48,15 +64,20 @@ module API ...@@ -48,15 +64,20 @@ module API
def self.update_params_at_least_one_of def self.update_params_at_least_one_of
[ [
:jobs_enabled, :auto_devops_enabled,
:resolve_outdated_diff_discussions, :auto_devops_deploy_strategy,
:auto_cancel_pending_pipelines,
:build_coverage_regex,
:build_git_strategy,
:build_timeout,
:builds_access_level,
: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 +85,24 @@ module API ...@@ -64,14 +85,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
......
# frozen_string_literal: true
require 'spec_helper'
describe ProjectAPICompatibility do
let(:project) { create(:project) }
# git_strategy
it "converts build_git_strategy=fetch to build_allow_git_fetch=true" do
project.update!(build_git_strategy: 'fetch')
expect(project.build_allow_git_fetch).to eq(true)
end
it "converts build_git_strategy=clone to build_allow_git_fetch=false" do
project.update!(build_git_strategy: 'clone')
expect(project.build_allow_git_fetch).to eq(false)
end
# auto_devops_enabled
it "converts auto_devops_enabled=false to auto_devops_enabled?=false" do
expect(project.auto_devops_enabled?).to eq(true)
project.update!(auto_devops_enabled: false)
expect(project.auto_devops_enabled?).to eq(false)
end
it "converts auto_devops_enabled=true to auto_devops_enabled?=true" do
expect(project.auto_devops_enabled?).to eq(true)
project.update!(auto_devops_enabled: true)
expect(project.auto_devops_enabled?).to eq(true)
end
# auto_devops_deploy_strategy
it "converts auto_devops_deploy_strategy=timed_incremental to auto_devops.deploy_strategy=timed_incremental" do
expect(project.auto_devops).to be_nil
project.update!(auto_devops_deploy_strategy: 'timed_incremental')
expect(project.auto_devops.deploy_strategy).to eq('timed_incremental')
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,34 @@ describe API::Projects do ...@@ -1913,6 +1919,34 @@ 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 build_git_strategy' do
project_param = { build_git_strategy: 'clone' }
put api("/projects/#{project3.id}", user), params: project_param
expect(response).to have_gitlab_http_status(200)
expect(json_response['build_git_strategy']).to eq('clone')
end
it 'rejects to update build_git_strategy when build_git_strategy is invalid' do
project_param = { build_git_strategy: 'invalid' }
put api("/projects/#{project3.id}", user), params: project_param
expect(response).to have_gitlab_http_status(400)
end
it 'updates merge_method' do it 'updates merge_method' do
project_param = { merge_method: 'ff' } project_param = { merge_method: 'ff' }
...@@ -1946,6 +1980,26 @@ describe API::Projects do ...@@ -1946,6 +1980,26 @@ describe API::Projects do
'-/system/project/avatar/'\ '-/system/project/avatar/'\
"#{project3.id}/banana_sample.gif") "#{project3.id}/banana_sample.gif")
end end
it 'updates auto_devops_deploy_strategy' do
project_param = { auto_devops_deploy_strategy: 'timed_incremental' }
put api("/projects/#{project3.id}", user), params: project_param
expect(response).to have_gitlab_http_status(200)
expect(json_response['auto_devops_deploy_strategy']).to eq('timed_incremental')
end
it 'updates auto_devops_enabled' do
project_param = { auto_devops_enabled: false }
put api("/projects/#{project3.id}", user), params: project_param
expect(response).to have_gitlab_http_status(200)
expect(json_response['auto_devops_enabled']).to eq(false)
end
end end
context 'when authenticated as project maintainer' do context 'when authenticated as project maintainer' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment