summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
commit8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch)
tree544930fb309b30317ae9797a9683768705d664c4 /lib/api
parent4b1de649d0168371549608993deac953eb692019 (diff)
downloadgitlab-ce-8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca.tar.gz
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/admin/instance_clusters.rb1
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/boards.rb2
-rw-r--r--lib/api/boards_responses.rb22
-rw-r--r--lib/api/ci/runner.rb4
-rw-r--r--lib/api/composer_packages.rb5
-rw-r--r--lib/api/conan_instance_packages.rb2
-rw-r--r--lib/api/conan_package_endpoints.rb351
-rw-r--r--lib/api/conan_project_packages.rb2
-rw-r--r--lib/api/concerns/packages/conan_endpoints.rb355
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb12
-rw-r--r--lib/api/concerns/packages/nuget_endpoints.rb135
-rw-r--r--lib/api/discussions.rb1
-rw-r--r--lib/api/entities/cluster.rb2
-rw-r--r--lib/api/entities/feature.rb10
-rw-r--r--lib/api/entities/feature_flag.rb4
-rw-r--r--lib/api/entities/issue.rb1
-rw-r--r--lib/api/entities/merge_request_basic.rb1
-rw-r--r--lib/api/entities/note.rb1
-rw-r--r--lib/api/entities/project.rb2
-rw-r--r--lib/api/entities/project_import_status.rb5
-rw-r--r--lib/api/entities/project_snippet.rb2
-rw-r--r--lib/api/entities/project_statistics.rb1
-rw-r--r--lib/api/entities/related_issue.rb2
-rw-r--r--lib/api/feature_flags.rb24
-rw-r--r--lib/api/feature_flags_user_lists.rb2
-rw-r--r--lib/api/features.rb28
-rwxr-xr-xlib/api/go_proxy.rb2
-rw-r--r--lib/api/group_boards.rb2
-rw-r--r--lib/api/group_clusters.rb2
-rw-r--r--lib/api/group_labels.rb2
-rw-r--r--lib/api/helpers.rb30
-rw-r--r--lib/api/helpers/internal_helpers.rb34
-rw-r--r--lib/api/helpers/members_helpers.rb2
-rw-r--r--lib/api/helpers/packages/basic_auth_helpers.rb4
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb13
-rw-r--r--lib/api/helpers/projects_helpers.rb4
-rw-r--r--lib/api/helpers/services_helpers.rb61
-rw-r--r--lib/api/helpers/sse_helpers.rb16
-rw-r--r--lib/api/internal/base.rb4
-rw-r--r--lib/api/internal/kubernetes.rb6
-rw-r--r--lib/api/internal/pages.rb43
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/jobs.rb2
-rw-r--r--lib/api/labels.rb4
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/api/merge_request_approvals.rb2
-rw-r--r--lib/api/merge_requests.rb3
-rw-r--r--lib/api/nuget_packages.rb247
-rw-r--r--lib/api/nuget_project_packages.rb139
-rw-r--r--lib/api/project_clusters.rb2
-rw-r--r--lib/api/project_repository_storage_moves.rb16
-rw-r--r--lib/api/pypi_packages.rb2
-rw-r--r--lib/api/release/links.rb2
-rw-r--r--lib/api/settings.rb6
-rw-r--r--lib/api/statistics.rb2
-rw-r--r--lib/api/usage_data.rb12
-rw-r--r--lib/api/users.rb18
-rw-r--r--lib/api/validations/validators/absence.rb2
-rw-r--r--lib/api/validations/validators/array_none_any.rb6
-rw-r--r--lib/api/validations/validators/check_assignees_count.rb7
-rw-r--r--lib/api/validations/validators/email_or_email_list.rb3
-rw-r--r--lib/api/validations/validators/file_path.rb6
-rw-r--r--lib/api/validations/validators/git_ref.rb6
-rw-r--r--lib/api/validations/validators/git_sha.rb6
-rw-r--r--lib/api/validations/validators/integer_none_any.rb12
-rw-r--r--lib/api/validations/validators/integer_or_custom_value.rb33
-rw-r--r--lib/api/validations/validators/limit.rb3
-rw-r--r--lib/api/validations/validators/untrusted_regexp.rb2
69 files changed, 1000 insertions, 751 deletions
diff --git a/lib/api/admin/instance_clusters.rb b/lib/api/admin/instance_clusters.rb
index 679e231b283..b724d3a38dc 100644
--- a/lib/api/admin/instance_clusters.rb
+++ b/lib/api/admin/instance_clusters.rb
@@ -76,6 +76,7 @@ module API
optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :domain, type: String, desc: 'Cluster base domain'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
+ optional :managed, type: Boolean, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ea149f25584..06c2b46a2f2 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -211,7 +211,7 @@ module API
mount ::API::ProjectPackages
mount ::API::GroupPackages
mount ::API::PackageFiles
- mount ::API::NugetPackages
+ mount ::API::NugetProjectPackages
mount ::API::PypiPackages
mount ::API::ComposerPackages
mount ::API::ConanProjectPackages
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index f4b23c507f4..e2d30dd7c2b 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -117,8 +117,6 @@ module API
use :list_creation_params
end
post '/lists' do
- authorize_list_type_resource!
-
authorize!(:admin_list, user_project)
create_list
diff --git a/lib/api/boards_responses.rb b/lib/api/boards_responses.rb
index 2ae82f78e01..89355c84401 100644
--- a/lib/api/boards_responses.rb
+++ b/lib/api/boards_responses.rb
@@ -45,21 +45,17 @@ module API
def create_list
create_list_service =
- ::Boards::Lists::CreateService.new(board_parent, current_user, create_list_params)
+ ::Boards::Lists::CreateService.new(board_parent, current_user, declared_params.compact.with_indifferent_access)
- list = create_list_service.execute(board)
+ response = create_list_service.execute(board)
- if list.valid?
- present list, with: Entities::List
+ if response.success?
+ present response.payload[:list], with: Entities::List
else
- render_validation_error!(list)
+ render_api_error!({ error: response.errors.first }, 400)
end
end
- def create_list_params
- params.slice(:label_id)
- end
-
def move_list(list)
move_list_service =
::Boards::Lists::MoveService.new(board_parent, current_user, { position: params[:position].to_i })
@@ -80,14 +76,6 @@ module API
end
end
- # rubocop: disable CodeReuse/ActiveRecord
- def authorize_list_type_resource!
- unless available_labels_for(board_parent).exists?(params[:label_id])
- render_api_error!({ error: 'Label not found!' }, 400)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
params :list_creation_params do
requires :label_id, type: Integer, desc: 'The ID of an existing label'
end
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 85232b4ae1b..86e1a939df1 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -176,6 +176,10 @@ module API
optional :state, type: String, desc: %q(Job's status: success, failed)
optional :checksum, type: String, desc: %q(Job's trace CRC32 checksum)
optional :failure_reason, type: String, desc: %q(Job's failure_reason)
+ optional :output, type: Hash, desc: %q(Build log state) do
+ optional :checksum, type: String, desc: %q(Job's trace CRC32 checksum)
+ optional :bytesize, type: Integer, desc: %q(Job's trace size in bytes)
+ end
end
put '/:id' do
job = authenticate_job!
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index 0ac5cc45ccf..1181650fe96 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -38,6 +38,8 @@ module API
packages = ::Packages::Composer::PackagesFinder.new(current_user, user_group).execute
if params[:package_name].present?
+ params[:package_name], params[:sha] = params[:package_name].split('$')
+
packages = packages.with_name(params[:package_name])
end
@@ -93,6 +95,7 @@ module API
get ':id/-/packages/composer/*package_name', requirements: COMPOSER_ENDPOINT_REQUIREMENTS, file_path: true do
not_found! if packages.empty?
+ not_found! if params[:sha].blank?
presenter.package_versions
end
@@ -132,7 +135,7 @@ module API
track_package_event('push_package', :composer)
::Packages::Composer::CreatePackageService
- .new(authorized_user_project, current_user, declared_params)
+ .new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job))
.execute
created!
diff --git a/lib/api/conan_instance_packages.rb b/lib/api/conan_instance_packages.rb
index 08265201328..8c13b580092 100644
--- a/lib/api/conan_instance_packages.rb
+++ b/lib/api/conan_instance_packages.rb
@@ -4,7 +4,7 @@
module API
class ConanInstancePackages < ::API::Base
namespace 'packages/conan/v1' do
- include ConanPackageEndpoints
+ include ::API::Concerns::Packages::ConanEndpoints
end
end
end
diff --git a/lib/api/conan_package_endpoints.rb b/lib/api/conan_package_endpoints.rb
deleted file mode 100644
index 188a42f26f8..00000000000
--- a/lib/api/conan_package_endpoints.rb
+++ /dev/null
@@ -1,351 +0,0 @@
-# frozen_string_literal: true
-
-# Conan Package Manager Client API
-#
-# These API endpoints are not consumed directly by users, so there is no documentation for the
-# individual endpoints. They are called by the Conan package manager client when users run commands
-# like `conan install` or `conan upload`. The usage of the GitLab Conan repository is documented here:
-# https://docs.gitlab.com/ee/user/packages/conan_repository/#installing-a-package
-#
-# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
-module API
- module ConanPackageEndpoints
- extend ActiveSupport::Concern
-
- PACKAGE_REQUIREMENTS = {
- package_name: API::NO_SLASH_URL_PART_REGEX,
- package_version: API::NO_SLASH_URL_PART_REGEX,
- package_username: API::NO_SLASH_URL_PART_REGEX,
- package_channel: API::NO_SLASH_URL_PART_REGEX
- }.freeze
-
- FILE_NAME_REQUIREMENTS = {
- file_name: API::NO_SLASH_URL_PART_REGEX
- }.freeze
-
- PACKAGE_COMPONENT_REGEX = Gitlab::Regex.conan_recipe_component_regex
- CONAN_REVISION_REGEX = Gitlab::Regex.conan_revision_regex
-
- CONAN_FILES = (Gitlab::Regex::Packages::CONAN_RECIPE_FILES + Gitlab::Regex::Packages::CONAN_PACKAGE_FILES).freeze
-
- included do
- feature_category :package_registry
-
- helpers ::API::Helpers::PackagesManagerClientsHelpers
- helpers ::API::Helpers::Packages::Conan::ApiHelpers
- helpers ::API::Helpers::RelatedResourcesHelpers
-
- before do
- require_packages_enabled!
-
- # Personal access token will be extracted from Bearer or Basic authorization
- # in the overridden find_personal_access_token or find_user_from_job_token helpers
- authenticate!
- end
-
- desc 'Ping the Conan API' do
- detail 'This feature was introduced in GitLab 12.2'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'ping' do
- header 'X-Conan-Server-Capabilities', [].join(',')
- end
-
- desc 'Search for packages' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- params do
- requires :q, type: String, desc: 'Search query'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'conans/search' do
- service = ::Packages::Conan::SearchService.new(current_user, query: params[:q]).execute
- service.payload
- end
-
- namespace 'users' do
- format :txt
-
- desc 'Authenticate user against conan CLI' do
- detail 'This feature was introduced in GitLab 12.2'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'authenticate' do
- unauthorized! unless token
-
- token.to_jwt
- end
-
- desc 'Check for valid user credentials per conan CLI' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'check_credentials' do
- authenticate!
- :ok
- end
- end
-
- params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
- end
- namespace 'conans/:package_name/:package_version/:package_username/:package_channel', requirements: PACKAGE_REQUIREMENTS do
- # Get the snapshot
- #
- # the snapshot is a hash of { filename: md5 hash }
- # md5 hash is the has of that file. This hash is used to diff the files existing on the client
- # to determine which client files need to be uploaded if no recipe exists the snapshot is empty
- desc 'Package Snapshot' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'packages/:conan_package_reference' do
- authorize!(:read_package, project)
-
- presenter = ::Packages::Conan::PackagePresenter.new(
- package,
- current_user,
- project,
- conan_package_reference: params[:conan_package_reference]
- )
-
- present presenter, with: ::API::Entities::ConanPackage::ConanPackageSnapshot
- end
-
- desc 'Recipe Snapshot' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get do
- authorize!(:read_package, project)
-
- presenter = ::Packages::Conan::PackagePresenter.new(package, current_user, project)
-
- present presenter, with: ::API::Entities::ConanPackage::ConanRecipeSnapshot
- end
-
- # Get the manifest
- # returns the download urls for the existing recipe in the registry
- #
- # the manifest is a hash of { filename: url }
- # where the url is the download url for the file
- desc 'Package Digest' do
- detail 'This feature was introduced in GitLab 12.5'
- end
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'packages/:conan_package_reference/digest' do
- present_package_download_urls
- end
-
- desc 'Recipe Digest' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'digest' do
- present_recipe_download_urls
- end
-
- # Get the download urls
- #
- # returns the download urls for the existing recipe or package in the registry
- #
- # the manifest is a hash of { filename: url }
- # where the url is the download url for the file
- desc 'Package Download Urls' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'packages/:conan_package_reference/download_urls' do
- present_package_download_urls
- end
-
- desc 'Recipe Download Urls' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'download_urls' do
- present_recipe_download_urls
- end
-
- # Get the upload urls
- #
- # request body contains { filename: filesize } where the filename is the
- # name of the file the conan client is requesting to upload
- #
- # returns { filename: url }
- # where the url is the upload url for the file that the conan client will use
- desc 'Package Upload Urls' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- post 'packages/:conan_package_reference/upload_urls' do
- authorize!(:read_package, project)
-
- status 200
- present package_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
- end
-
- desc 'Recipe Upload Urls' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- post 'upload_urls' do
- authorize!(:read_package, project)
-
- status 200
- present recipe_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
- end
-
- desc 'Delete Package' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- delete do
- authorize!(:destroy_package, project)
-
- track_package_event('delete_package', :conan, category: 'API::ConanPackages')
-
- package.destroy
- end
- end
-
- params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
- requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision'
- end
- namespace 'files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision', requirements: PACKAGE_REQUIREMENTS do
- before do
- authenticate_non_get!
- end
-
- params do
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
- end
- namespace 'export/:file_name', requirements: FILE_NAME_REQUIREMENTS do
- desc 'Download recipe files' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get do
- download_package_file(:recipe_file)
- end
-
- desc 'Upload recipe package files' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- params do
- requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put do
- upload_package_file(:recipe_file)
- end
-
- desc 'Workhorse authorize the conan recipe file' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put 'authorize' do
- authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
- end
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan Package ID'
- requires :package_revision, type: String, desc: 'Conan Package Revision'
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
- end
- namespace 'package/:conan_package_reference/:package_revision/:file_name', requirements: FILE_NAME_REQUIREMENTS do
- desc 'Download package files' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get do
- download_package_file(:package_file)
- end
-
- desc 'Workhorse authorize the conan package file' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put 'authorize' do
- authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
- end
-
- desc 'Upload package files' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- params do
- requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put do
- upload_package_file(:package_file)
- end
- end
- end
- end
- end
-end
diff --git a/lib/api/conan_project_packages.rb b/lib/api/conan_project_packages.rb
index db8cd187811..636b5dca5ed 100644
--- a/lib/api/conan_project_packages.rb
+++ b/lib/api/conan_project_packages.rb
@@ -9,7 +9,7 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/conan/v1' do
- include ConanPackageEndpoints
+ include ::API::Concerns::Packages::ConanEndpoints
end
end
end
diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb
new file mode 100644
index 00000000000..6c8b3a1ba4a
--- /dev/null
+++ b/lib/api/concerns/packages/conan_endpoints.rb
@@ -0,0 +1,355 @@
+# frozen_string_literal: true
+
+# Conan Package Manager Client API
+#
+# These API endpoints are not consumed directly by users, so there is no documentation for the
+# individual endpoints. They are called by the Conan package manager client when users run commands
+# like `conan install` or `conan upload`. The usage of the GitLab Conan repository is documented here:
+# https://docs.gitlab.com/ee/user/packages/conan_repository/#installing-a-package
+#
+# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
+module API
+ module Concerns
+ module Packages
+ module ConanEndpoints
+ extend ActiveSupport::Concern
+
+ PACKAGE_REQUIREMENTS = {
+ package_name: API::NO_SLASH_URL_PART_REGEX,
+ package_version: API::NO_SLASH_URL_PART_REGEX,
+ package_username: API::NO_SLASH_URL_PART_REGEX,
+ package_channel: API::NO_SLASH_URL_PART_REGEX
+ }.freeze
+
+ FILE_NAME_REQUIREMENTS = {
+ file_name: API::NO_SLASH_URL_PART_REGEX
+ }.freeze
+
+ PACKAGE_COMPONENT_REGEX = Gitlab::Regex.conan_recipe_component_regex
+ CONAN_REVISION_REGEX = Gitlab::Regex.conan_revision_regex
+
+ CONAN_FILES = (Gitlab::Regex::Packages::CONAN_RECIPE_FILES + Gitlab::Regex::Packages::CONAN_PACKAGE_FILES).freeze
+
+ included do
+ feature_category :package_registry
+
+ helpers ::API::Helpers::PackagesManagerClientsHelpers
+ helpers ::API::Helpers::Packages::Conan::ApiHelpers
+ helpers ::API::Helpers::RelatedResourcesHelpers
+
+ before do
+ require_packages_enabled!
+
+ # Personal access token will be extracted from Bearer or Basic authorization
+ # in the overridden find_personal_access_token or find_user_from_job_token helpers
+ authenticate!
+ end
+
+ desc 'Ping the Conan API' do
+ detail 'This feature was introduced in GitLab 12.2'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'ping' do
+ header 'X-Conan-Server-Capabilities', [].join(',')
+ end
+
+ desc 'Search for packages' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ params do
+ requires :q, type: String, desc: 'Search query'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'conans/search' do
+ service = ::Packages::Conan::SearchService.new(current_user, query: params[:q]).execute
+ service.payload
+ end
+
+ namespace 'users' do
+ format :txt
+
+ desc 'Authenticate user against conan CLI' do
+ detail 'This feature was introduced in GitLab 12.2'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'authenticate' do
+ unauthorized! unless token
+
+ token.to_jwt
+ end
+
+ desc 'Check for valid user credentials per conan CLI' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'check_credentials' do
+ authenticate!
+ :ok
+ end
+ end
+
+ params do
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
+ requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
+ requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
+ end
+ namespace 'conans/:package_name/:package_version/:package_username/:package_channel', requirements: PACKAGE_REQUIREMENTS do
+ # Get the snapshot
+ #
+ # the snapshot is a hash of { filename: md5 hash }
+ # md5 hash is the has of that file. This hash is used to diff the files existing on the client
+ # to determine which client files need to be uploaded if no recipe exists the snapshot is empty
+ desc 'Package Snapshot' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'packages/:conan_package_reference' do
+ authorize!(:read_package, project)
+
+ presenter = ::Packages::Conan::PackagePresenter.new(
+ package,
+ current_user,
+ project,
+ conan_package_reference: params[:conan_package_reference]
+ )
+
+ present presenter, with: ::API::Entities::ConanPackage::ConanPackageSnapshot
+ end
+
+ desc 'Recipe Snapshot' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get do
+ authorize!(:read_package, project)
+
+ presenter = ::Packages::Conan::PackagePresenter.new(package, current_user, project)
+
+ present presenter, with: ::API::Entities::ConanPackage::ConanRecipeSnapshot
+ end
+
+ # Get the manifest
+ # returns the download urls for the existing recipe in the registry
+ #
+ # the manifest is a hash of { filename: url }
+ # where the url is the download url for the file
+ desc 'Package Digest' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'packages/:conan_package_reference/digest' do
+ present_package_download_urls
+ end
+
+ desc 'Recipe Digest' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'digest' do
+ present_recipe_download_urls
+ end
+
+ # Get the download urls
+ #
+ # returns the download urls for the existing recipe or package in the registry
+ #
+ # the manifest is a hash of { filename: url }
+ # where the url is the download url for the file
+ desc 'Package Download Urls' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'packages/:conan_package_reference/download_urls' do
+ present_package_download_urls
+ end
+
+ desc 'Recipe Download Urls' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'download_urls' do
+ present_recipe_download_urls
+ end
+
+ # Get the upload urls
+ #
+ # request body contains { filename: filesize } where the filename is the
+ # name of the file the conan client is requesting to upload
+ #
+ # returns { filename: url }
+ # where the url is the upload url for the file that the conan client will use
+ desc 'Package Upload Urls' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ post 'packages/:conan_package_reference/upload_urls' do
+ authorize!(:read_package, project)
+
+ status 200
+ present package_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
+ end
+
+ desc 'Recipe Upload Urls' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ post 'upload_urls' do
+ authorize!(:read_package, project)
+
+ status 200
+ present recipe_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
+ end
+
+ desc 'Delete Package' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ delete do
+ authorize!(:destroy_package, project)
+
+ track_package_event('delete_package', :conan, category: 'API::ConanPackages')
+
+ package.destroy
+ end
+ end
+
+ params do
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
+ requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
+ requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
+ requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision'
+ end
+ namespace 'files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision', requirements: PACKAGE_REQUIREMENTS do
+ before do
+ authenticate_non_get!
+ end
+
+ params do
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ end
+ namespace 'export/:file_name', requirements: FILE_NAME_REQUIREMENTS do
+ desc 'Download recipe files' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get do
+ download_package_file(:recipe_file)
+ end
+
+ desc 'Upload recipe package files' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ params do
+ requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put do
+ upload_package_file(:recipe_file)
+ end
+
+ desc 'Workhorse authorize the conan recipe file' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put 'authorize' do
+ authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
+ end
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan Package ID'
+ requires :package_revision, type: String, desc: 'Conan Package Revision'
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ end
+ namespace 'package/:conan_package_reference/:package_revision/:file_name', requirements: FILE_NAME_REQUIREMENTS do
+ desc 'Download package files' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get do
+ download_package_file(:package_file)
+ end
+
+ desc 'Workhorse authorize the conan package file' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put 'authorize' do
+ authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
+ end
+
+ desc 'Upload package files' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ params do
+ requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put do
+ upload_package_file(:package_file)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index a91db93b182..833288c6013 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -37,7 +37,7 @@ module API
get 'dist-tags', format: false, requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
package_name = params[:package_name]
- bad_request!('Package Name') if package_name.blank?
+ bad_request_missing_attribute!('Package Name') if package_name.blank?
authorize_read_package!(project)
@@ -62,9 +62,9 @@ module API
version = env['api.request.body']
tag = params[:tag]
- bad_request!('Package Name') if package_name.blank?
- bad_request!('Version') if version.blank?
- bad_request!('Tag') if tag.blank?
+ bad_request_missing_attribute!('Package Name') if package_name.blank?
+ bad_request_missing_attribute!('Version') if version.blank?
+ bad_request_missing_attribute!('Tag') if tag.blank?
authorize_create_package!(project)
@@ -85,8 +85,8 @@ module API
package_name = params[:package_name]
tag = params[:tag]
- bad_request!('Package Name') if package_name.blank?
- bad_request!('Tag') if tag.blank?
+ bad_request_missing_attribute!('Package Name') if package_name.blank?
+ bad_request_missing_attribute!('Tag') if tag.blank?
authorize_destroy_package!(project)
diff --git a/lib/api/concerns/packages/nuget_endpoints.rb b/lib/api/concerns/packages/nuget_endpoints.rb
new file mode 100644
index 00000000000..5177c4d23c0
--- /dev/null
+++ b/lib/api/concerns/packages/nuget_endpoints.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+#
+# NuGet Package Manager Client API
+#
+# These API endpoints are not consumed directly by users, so there is no documentation for the
+# individual endpoints. They are called by the NuGet package manager client when users run commands
+# like `nuget install` or `nuget push`. The usage of the GitLab NuGet registry is documented here:
+# https://docs.gitlab.com/ee/user/packages/nuget_repository/
+#
+# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
+module API
+ module Concerns
+ module Packages
+ module NugetEndpoints
+ extend ActiveSupport::Concern
+
+ POSITIVE_INTEGER_REGEX = %r{\A[1-9]\d*\z}.freeze
+ NON_NEGATIVE_INTEGER_REGEX = %r{\A0|[1-9]\d*\z}.freeze
+
+ included do
+ helpers do
+ def find_packages
+ packages = package_finder.execute
+
+ not_found!('Packages') unless packages.exists?
+
+ packages
+ end
+
+ def find_package
+ package = package_finder(package_version: params[:package_version]).execute
+ .first
+
+ not_found!('Package') unless package
+
+ package
+ end
+
+ def package_finder(finder_params = {})
+ ::Packages::Nuget::PackageFinder.new(
+ authorized_user_project,
+ **finder_params.merge(package_name: params[:package_name])
+ )
+ end
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/service-index
+ desc 'The NuGet Service Index' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get 'index', format: :json do
+ authorize_read_package!(authorized_user_project)
+ track_package_event('cli_metadata', :nuget, category: 'API::NugetPackages')
+
+ present ::Packages::Nuget::ServiceIndexPresenter.new(authorized_user_project),
+ with: ::API::Entities::Nuget::ServiceIndex
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
+ params do
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+ namespace '/metadata/*package_name' do
+ before do
+ authorize_read_package!(authorized_user_project)
+ end
+
+ desc 'The NuGet Metadata Service - Package name level' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get 'index', format: :json do
+ present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages),
+ with: ::API::Entities::Nuget::PackagesMetadata
+ end
+
+ desc 'The NuGet Metadata Service - Package name and version level' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+ params do
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get '*package_version', format: :json do
+ present ::Packages::Nuget::PackageMetadataPresenter.new(find_package),
+ with: ::API::Entities::Nuget::PackageMetadata
+ end
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
+ params do
+ requires :q, type: String, desc: 'The search term'
+ optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX
+ optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX
+ optional :prerelease, type: ::Grape::API::Boolean, desc: 'Include prerelease versions', default: true
+ end
+ namespace '/query' do
+ before do
+ authorize_read_package!(authorized_user_project)
+ end
+
+ desc 'The NuGet Search Service' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get format: :json do
+ search_options = {
+ include_prerelease_versions: params[:prerelease],
+ per_page: params[:take],
+ padding: params[:skip]
+ }
+ search = ::Packages::Nuget::SearchService
+ .new(authorized_user_project, params[:q], search_options)
+ .execute
+
+ track_package_event('search_package', :nuget, category: 'API::NugetPackages')
+
+ present ::Packages::Nuget::SearchResultsPresenter.new(search),
+ with: ::API::Entities::Nuget::SearchResults
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 4c4ec200060..580d546b360 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -104,6 +104,7 @@ module API
position: params[:position],
id_key => noteable.id
}
+ opts[:commit_id] = params[:commit_id] if noteable.is_a?(MergeRequest) && type == 'DiffNote'
note = create_note(noteable, opts)
diff --git a/lib/api/entities/cluster.rb b/lib/api/entities/cluster.rb
index 67459092a33..b7e76e763f7 100644
--- a/lib/api/entities/cluster.rb
+++ b/lib/api/entities/cluster.rb
@@ -3,7 +3,7 @@
module API
module Entities
class Cluster < Grape::Entity
- expose :id, :name, :created_at, :domain
+ expose :id, :name, :created_at, :domain, :enabled, :managed
expose :provider_type, :platform_type, :environment_scope, :cluster_type, :namespace_per_environment
expose :user, using: Entities::UserBasic
expose :platform_kubernetes, using: Entities::Platform::Kubernetes
diff --git a/lib/api/entities/feature.rb b/lib/api/entities/feature.rb
index 618a7be9c7b..d1151849cd7 100644
--- a/lib/api/entities/feature.rb
+++ b/lib/api/entities/feature.rb
@@ -17,6 +17,16 @@ module API
{ key: gate.key, value: value }
end.compact
end
+
+ class Definition < Grape::Entity
+ ::Feature::Definition::PARAMS.each do |param|
+ expose param
+ end
+ end
+
+ expose :definition, using: Definition do |feature|
+ ::Feature::Definition.definitions[feature.name.to_sym]
+ end
end
end
end
diff --git a/lib/api/entities/feature_flag.rb b/lib/api/entities/feature_flag.rb
index 82fdb20af00..f383eabd5dc 100644
--- a/lib/api/entities/feature_flag.rb
+++ b/lib/api/entities/feature_flag.rb
@@ -6,11 +6,11 @@ module API
expose :name
expose :description
expose :active
- expose :version, if: :feature_flags_new_version_enabled
+ expose :version
expose :created_at
expose :updated_at
expose :scopes, using: FeatureFlag::LegacyScope
- expose :strategies, using: FeatureFlag::Strategy, if: :feature_flags_new_version_enabled
+ expose :strategies, using: FeatureFlag::Strategy
end
end
end
diff --git a/lib/api/entities/issue.rb b/lib/api/entities/issue.rb
index 5f2609cf68b..82102854394 100644
--- a/lib/api/entities/issue.rb
+++ b/lib/api/entities/issue.rb
@@ -43,6 +43,7 @@ module API
end
expose :moved_to_id
+ expose :service_desk_reply_to
end
end
end
diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb
index 69523e3637b..7f1b5b87725 100644
--- a/lib/api/entities/merge_request_basic.rb
+++ b/lib/api/entities/merge_request_basic.rb
@@ -27,6 +27,7 @@ module API
expose(:downvotes) { |merge_request, options| issuable_metadata.downvotes }
expose :author, :assignees, :assignee, using: Entities::UserBasic
+ expose :reviewers, if: -> (merge_request, _) { merge_request.allows_reviewers? }, using: Entities::UserBasic
expose :source_project_id, :target_project_id
expose :labels do |merge_request, options|
if options[:with_labels_details]
diff --git a/lib/api/entities/note.rb b/lib/api/entities/note.rb
index f22ab73afd0..9a60c04220d 100644
--- a/lib/api/entities/note.rb
+++ b/lib/api/entities/note.rb
@@ -14,6 +14,7 @@ module API
expose :created_at, :updated_at
expose :system?, as: :system
expose :noteable_id, :noteable_type
+ expose :commit_id, if: ->(note, options) { note.noteable_type == "MergeRequest" && note.is_a?(DiffNote) }
expose :position, if: ->(note, options) { note.is_a?(DiffNote) } do |note|
note.position.to_h
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 82a44c75382..317caefe0a1 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -67,6 +67,8 @@ module API
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(:pages_access_level) { |project, options| project.project_feature.string_access_level(:pages) }
+ expose(:operations_access_level) { |project, options| project.project_feature.string_access_level(:operations) }
+ expose(:analytics_access_level) { |project, options| project.project_feature.string_access_level(:analytics) }
expose :emails_disabled
expose :shared_runners_enabled
diff --git a/lib/api/entities/project_import_status.rb b/lib/api/entities/project_import_status.rb
index f92593da3fa..e79c1cdf1a2 100644
--- a/lib/api/entities/project_import_status.rb
+++ b/lib/api/entities/project_import_status.rb
@@ -12,9 +12,8 @@ module API
project.import_state&.relation_hard_failures(limit: 100) || []
end
- # TODO: Use `expose_nil` once we upgrade the grape-entity gem
- expose :import_error, if: lambda { |project, _ops| project.import_state&.last_error } do |project|
- project.import_state.last_error
+ expose :import_error do |project, _options|
+ project.import_state&.last_error
end
end
end
diff --git a/lib/api/entities/project_snippet.rb b/lib/api/entities/project_snippet.rb
index 8ed87e51375..253fcfcf38f 100644
--- a/lib/api/entities/project_snippet.rb
+++ b/lib/api/entities/project_snippet.rb
@@ -1,4 +1,4 @@
-# frozen_String_literal: true
+# frozen_string_literal: true
module API
module Entities
diff --git a/lib/api/entities/project_statistics.rb b/lib/api/entities/project_statistics.rb
index 32201e88eaf..70980e670b0 100644
--- a/lib/api/entities/project_statistics.rb
+++ b/lib/api/entities/project_statistics.rb
@@ -10,6 +10,7 @@ module API
expose :lfs_objects_size
expose :build_artifacts_size, as: :job_artifacts_size
expose :snippets_size
+ expose :packages_size
end
end
end
diff --git a/lib/api/entities/related_issue.rb b/lib/api/entities/related_issue.rb
index 491c606bd49..60793fed5e0 100644
--- a/lib/api/entities/related_issue.rb
+++ b/lib/api/entities/related_issue.rb
@@ -5,6 +5,8 @@ module API
class RelatedIssue < ::API::Entities::Issue
expose :issue_link_id
expose :issue_link_type, as: :link_type
+ expose :issue_link_created_at, as: :link_created_at
+ expose :issue_link_updated_at, as: :link_updated_at
end
end
end
diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb
index 67168ba9be6..6fdc4535be3 100644
--- a/lib/api/feature_flags.rb
+++ b/lib/api/feature_flags.rb
@@ -62,8 +62,6 @@ module API
attrs = declared_params(include_missing: false)
- ensure_post_version_2_flags_enabled! if attrs[:version] == 'new_version_flag'
-
rename_key(attrs, :scopes, :scopes_attributes)
rename_key(attrs, :strategies, :strategies_attributes)
update_value(attrs, :strategies_attributes) do |strategies|
@@ -143,7 +141,7 @@ module API
end
desc 'Update a feature flag' do
- detail 'This feature will be introduced in GitLab 13.1 if feature_flags_new_version feature flag is removed'
+ detail 'This feature was introduced in GitLab 13.2'
success ::API::Entities::FeatureFlag
end
params do
@@ -163,7 +161,6 @@ module API
end
end
put do
- not_found! unless feature_flags_new_version_enabled?
authorize_update_feature_flag!
render_api_error!('PUT operations are not supported for legacy feature flags', :unprocessable_entity) if feature_flag.legacy_flag?
@@ -228,32 +225,17 @@ module API
def present_entity(result)
present result,
- with: ::API::Entities::FeatureFlag,
- feature_flags_new_version_enabled: feature_flags_new_version_enabled?
- end
-
- def ensure_post_version_2_flags_enabled!
- unless feature_flags_new_version_enabled?
- render_api_error!('Version 2 flags are not enabled for this project', :unprocessable_entity)
- end
+ with: ::API::Entities::FeatureFlag
end
def feature_flag
- @feature_flag ||= if feature_flags_new_version_enabled?
- user_project.operations_feature_flags.find_by_name!(params[:feature_flag_name])
- else
- user_project.operations_feature_flags.legacy_flag.find_by_name!(params[:feature_flag_name])
- end
+ @feature_flag ||= user_project.operations_feature_flags.find_by_name!(params[:feature_flag_name])
end
def new_version_flag_present?
user_project.operations_feature_flags.new_version_flag.find_by_name(params[:name]).present?
end
- def feature_flags_new_version_enabled?
- Feature.enabled?(:feature_flags_new_version, user_project, default_enabled: true)
- end
-
def rename_key(hash, old_key, new_key)
hash[new_key] = hash.delete(old_key) if hash.key?(old_key)
hash
diff --git a/lib/api/feature_flags_user_lists.rb b/lib/api/feature_flags_user_lists.rb
index 086bcbcdc89..8577da173b1 100644
--- a/lib/api/feature_flags_user_lists.rb
+++ b/lib/api/feature_flags_user_lists.rb
@@ -54,7 +54,7 @@ module API
end
params do
- requires :iid, type: String, desc: 'The internal id of the user list'
+ requires :iid, type: String, desc: 'The internal ID of the user list'
end
resource 'feature_flags_user_lists/:iid' do
desc 'Get a single feature flag user list belonging to a project' do
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 2c2e3e3d0c9..57bd7c38ad2 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -46,6 +46,15 @@ module API
present features, with: Entities::Feature, current_user: current_user
end
+ desc 'Get a list of all feature definitions' do
+ success Entities::Feature::Definition
+ end
+ get :definitions do
+ definitions = ::Feature::Definition.definitions.values.map(&:to_h)
+
+ present definitions, with: Entities::Feature::Definition, current_user: current_user
+ end
+
desc 'Set the gate value for the given feature' do
success Entities::Feature
end
@@ -56,6 +65,7 @@ module API
optional :user, type: String, desc: 'A GitLab username'
optional :group, type: String, desc: "A GitLab group's path, such as 'gitlab-org'"
optional :project, type: String, desc: 'A projects path, like gitlab-org/gitlab-ce'
+ optional :force, type: Boolean, desc: 'Skip feature flag validation checks, ie. YAML definition'
mutually_exclusive :key, :feature_group
mutually_exclusive :key, :user
@@ -63,9 +73,8 @@ module API
mutually_exclusive :key, :project
end
post ':name' do
- validate_feature_flag_name!(params[:name])
+ validate_feature_flag_name!(params[:name]) unless params[:force]
- feature = Feature.get(params[:name]) # rubocop:disable Gitlab/AvoidFeatureGet
targets = gate_targets(params)
value = gate_value(params)
key = gate_key(params)
@@ -73,25 +82,26 @@ module API
case value
when true
if gate_specified?(params)
- targets.each { |target| feature.enable(target) }
+ targets.each { |target| Feature.enable(params[:name], target) }
else
- feature.enable
+ Feature.enable(params[:name])
end
when false
if gate_specified?(params)
- targets.each { |target| feature.disable(target) }
+ targets.each { |target| Feature.disable(params[:name], target) }
else
- feature.disable
+ Feature.disable(params[:name])
end
else
if key == :percentage_of_actors
- feature.enable_percentage_of_actors(value)
+ Feature.enable_percentage_of_actors(params[:name], value)
else
- feature.enable_percentage_of_time(value)
+ Feature.enable_percentage_of_time(params[:name], value)
end
end
- present feature, with: Entities::Feature, current_user: current_user
+ present Feature.get(params[:name]), # rubocop:disable Gitlab/AvoidFeatureGet
+ with: Entities::Feature, current_user: current_user
end
desc 'Remove the gate value for the given feature'
diff --git a/lib/api/go_proxy.rb b/lib/api/go_proxy.rb
index 8fb4c561c40..2d978019f2a 100755
--- a/lib/api/go_proxy.rb
+++ b/lib/api/go_proxy.rb
@@ -48,7 +48,7 @@ module API
not_found! unless Feature.enabled?(:go_proxy, user_project)
module_name = case_decode params[:module_name]
- bad_request!('Module Name') if module_name.blank?
+ bad_request_missing_attribute!('Module Name') if module_name.blank?
mod = ::Packages::Go::ModuleFinder.new(user_project, module_name).execute
diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb
index ac5a1a2ce94..2bfd98a5b69 100644
--- a/lib/api/group_boards.rb
+++ b/lib/api/group_boards.rb
@@ -83,8 +83,6 @@ module API
use :list_creation_params
end
post '/lists' do
- authorize_list_type_resource!
-
authorize!(:admin_list, user_group)
create_list
diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb
index a435b050042..81944a653c8 100644
--- a/lib/api/group_clusters.rb
+++ b/lib/api/group_clusters.rb
@@ -75,10 +75,12 @@ module API
params do
requires :cluster_id, type: Integer, desc: 'The cluster ID'
optional :name, type: String, desc: 'Cluster name'
+ optional :enabled, type: Boolean, desc: 'Determines if cluster is active or not'
optional :domain, type: String, desc: 'Cluster base domain'
optional :environment_scope, type: String, desc: 'The associated environment to the cluster'
optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
+ optional :managed, type: Boolean, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index bf3ac8800b7..7fbf4445116 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -66,7 +66,7 @@ module API
success Entities::GroupLabel
end
params do
- optional :label_id, type: Integer, desc: 'The id of the label to be updated'
+ optional :label_id, type: Integer, desc: 'The ID of the label to be updated'
optional :name, type: String, desc: 'The name of the label to be updated'
use :group_label_update_params
exactly_one_of :label_id, :name
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 147d8407142..6fe25471289 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -271,6 +271,10 @@ module API
authorize! :read_build, user_project
end
+ def authorize_read_build_trace!(build)
+ authorize! :read_build_trace, build
+ end
+
def authorize_destroy_artifacts!
authorize! :destroy_artifacts, user_project
end
@@ -318,7 +322,7 @@ module API
# keys (required) - A hash consisting of keys that must be present
def required_attributes!(keys)
keys.each do |key|
- bad_request!(key) unless params[key].present?
+ bad_request_missing_attribute!(key) unless params[key].present?
end
end
@@ -364,12 +368,16 @@ module API
render_api_error!(message.join(' '), 403)
end
- def bad_request!(attribute)
- message = ["400 (Bad request)"]
- message << "\"" + attribute.to_s + "\" not given" if attribute
+ def bad_request!(reason = nil)
+ message = ['400 Bad request']
+ message << "- #{reason}" if reason
render_api_error!(message.join(' '), 400)
end
+ def bad_request_missing_attribute!(attribute)
+ bad_request!("\"#{attribute}\" not given")
+ end
+
def not_found!(resource = nil)
message = ["404"]
message << resource if resource
@@ -536,13 +544,23 @@ module API
)
end
+ def increment_counter(event_name)
+ feature_name = "usage_data_#{event_name}"
+ return unless Feature.enabled?(feature_name)
+
+ Gitlab::UsageDataCounters.count(event_name)
+ rescue => error
+ Gitlab::AppLogger.warn("Redis tracking event failed for event: #{event_name}, message: #{error.message}")
+ end
+
# @param event_name [String] the event name
# @param values [Array|String] the values counted
def increment_unique_values(event_name, values)
return unless values.present?
- feature_name = "usage_data_#{event_name}"
- return unless Feature.enabled?(feature_name)
+ feature_flag = "usage_data_#{event_name}"
+
+ return unless Feature.enabled?(feature_flag, default_enabled: true)
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(values, event_name)
rescue => error
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 69b53ea6c2f..12b0a053e79 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -31,8 +31,7 @@ module API
def access_checker_for(actor, protocol)
access_checker_klass.new(actor.key_or_user, container, protocol,
authentication_abilities: ssh_authentication_abilities,
- namespace_path: namespace_path,
- repository_path: project_path,
+ repository_path: repository_path,
redirected_path: redirected_path)
end
@@ -71,18 +70,22 @@ module API
false
end
- def project_path
- project&.path || project_path_match[:project_path]
- end
-
- def namespace_path
- project&.namespace&.full_path || project_path_match[:namespace_path]
- end
-
private
- def project_path_match
- @project_path_match ||= params[:project].match(Gitlab::PathRegex.full_project_git_path_regex) || {}
+ def repository_path
+ if container
+ "#{container.full_path}.git"
+ elsif params[:project]
+ # When the project doesn't exist, we still need to pass on the path
+ # to support auto-creation in `GitAccessProject`.
+ #
+ # For consistency with the Git HTTP controllers, we normalize the path
+ # to remove a leading slash and ensure a trailing `.git`.
+ #
+ # NOTE: For GitLab Shell, `params[:project]` is the full repository path
+ # from the SSH command, with an optional trailing `.git`.
+ "#{params[:project].delete_prefix('/').delete_suffix('.git')}.git"
+ end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
@@ -96,7 +99,7 @@ module API
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
- # Project id to pass between components that don't share/don't have
+ # Repository id to pass between components that don't share/don't have
# access to the same filesystem mounts
def gl_repository
repo_type.identifier_for_container(container)
@@ -106,8 +109,9 @@ module API
repository.full_path
end
- # Return the repository depending on whether we want the wiki or the
- # regular repository
+ # Return the repository for the detected type and container
+ #
+ # @returns [Repository]
def repository
@repository ||= repo_type.repository_for(container)
end
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index 431001c227d..8aed578905e 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -45,7 +45,7 @@ module API
end
def find_all_members_for_project(project)
- MembersFinder.new(project, current_user).execute(include_relations: [:inherited, :direct, :invited_groups_members])
+ MembersFinder.new(project, current_user).execute(include_relations: [:inherited, :direct, :invited_groups])
end
def find_all_members_for_group(group)
diff --git a/lib/api/helpers/packages/basic_auth_helpers.rb b/lib/api/helpers/packages/basic_auth_helpers.rb
index e35a8712131..0784efc11d6 100644
--- a/lib/api/helpers/packages/basic_auth_helpers.rb
+++ b/lib/api/helpers/packages/basic_auth_helpers.rb
@@ -7,8 +7,8 @@ module API
extend ::Gitlab::Utils::Override
module Constants
- AUTHENTICATE_REALM_HEADER = 'Www-Authenticate: Basic realm'
- AUTHENTICATE_REALM_NAME = 'GitLab Packages Registry'
+ AUTHENTICATE_REALM_HEADER = 'WWW-Authenticate'
+ AUTHENTICATE_REALM_NAME = 'Basic realm="GitLab Packages Registry"'
end
include Constants
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index 934e18bdd0a..39ecfc171a9 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -164,7 +164,11 @@ module API
end
def find_or_create_package
- package || ::Packages::Conan::CreatePackageService.new(project, current_user, params).execute
+ package || ::Packages::Conan::CreatePackageService.new(
+ project,
+ current_user,
+ params.merge(build: current_authenticated_job)
+ ).execute
end
def track_push_package_event
@@ -184,7 +188,11 @@ module API
def create_package_file_with_type(file_type, current_package)
unless params[:file].size == 0 # rubocop: disable Style/ZeroLengthPredicate
# conan sends two upload requests, the first has no file, so we skip record creation if file.size == 0
- ::Packages::Conan::CreatePackageFileService.new(current_package, params[:file], params.merge(conan_file_type: file_type)).execute
+ ::Packages::Conan::CreatePackageFileService.new(
+ current_package,
+ params[:file],
+ params.merge(conan_file_type: file_type, build: current_authenticated_job)
+ ).execute
end
end
@@ -214,6 +222,7 @@ module API
return unless route_authentication_setting[:job_token_allowed]
job = find_job_from_token || raise(::Gitlab::Auth::UnauthorizedError)
+ @current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables
job.user
end
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 0364ba2ad9e..f5f45cf7351 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -6,7 +6,7 @@ module API
extend ActiveSupport::Concern
extend Grape::API::Helpers
- STATISTICS_SORT_PARAMS = %w[storage_size repository_size wiki_size].freeze
+ STATISTICS_SORT_PARAMS = %w[storage_size repository_size wiki_size packages_size].freeze
params :optional_project_params_ce do
optional :description, type: String, desc: 'The description of the project'
@@ -32,6 +32,8 @@ module API
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 :pages_access_level, type: String, values: %w(disabled private enabled public), desc: 'Pages access level. One of `disabled`, `private`, `enabled` or `public`'
+ optional :operations_access_level, type: String, values: %w(disabled private enabled), desc: 'Operations access level. One of `disabled`, `private` or `enabled`'
+ optional :analytics_access_level, type: String, values: %w(disabled private enabled), desc: 'Analytics access level. One of `disabled`, `private` or `enabled`'
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 4adb27a7414..9d2fd9978d9 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -304,6 +304,38 @@ module API
desc: 'Project URL'
}
],
+ 'datadog' => [
+ {
+ required: true,
+ name: :api_key,
+ type: String,
+ desc: 'API key used for authentication with Datadog'
+ },
+ {
+ required: false,
+ name: :datadog_site,
+ type: String,
+ desc: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site'
+ },
+ {
+ required: false,
+ name: :api_url,
+ type: String,
+ desc: '(Advanced) Define the full URL for your Datadog site directly'
+ },
+ {
+ required: false,
+ name: :datadog_service,
+ type: String,
+ desc: 'Name of this GitLab instance that all data will be tagged with'
+ },
+ {
+ required: false,
+ name: :datadog_env,
+ type: String,
+ desc: 'The environment tag that traces will be tagged with'
+ }
+ ],
'discord' => [
{
required: true,
@@ -459,6 +491,32 @@ module API
desc: 'Colorize messages'
}
],
+ 'jenkins' => [
+ {
+ required: true,
+ name: :jenkins_url,
+ type: String,
+ desc: 'Jenkins root URL like https://jenkins.example.com'
+ },
+ {
+ required: true,
+ name: :project_name,
+ type: String,
+ desc: 'The URL-friendly project name. Example: my_project_name'
+ },
+ {
+ required: false,
+ name: :username,
+ type: String,
+ desc: 'A user with access to the Jenkins server, if applicable'
+ },
+ {
+ required: false,
+ name: :password,
+ type: String,
+ desc: 'The password of the user'
+ }
+ ],
'jira' => [
{
required: true,
@@ -758,6 +816,7 @@ module API
::ConfluenceService,
::CampfireService,
::CustomIssueTrackerService,
+ ::DatadogService,
::DiscordService,
::DroneCiService,
::EmailsOnPushService,
@@ -767,6 +826,7 @@ module API
::HangoutsChatService,
::HipchatService,
::IrkerService,
+ ::JenkinsService,
::JiraService,
::MattermostSlashCommandsService,
::SlackSlashCommandsService,
@@ -787,7 +847,6 @@ module API
def self.development_service_classes
[
::MockCiService,
- ::MockDeploymentService,
::MockMonitoringService
]
end
diff --git a/lib/api/helpers/sse_helpers.rb b/lib/api/helpers/sse_helpers.rb
new file mode 100644
index 00000000000..c354694f508
--- /dev/null
+++ b/lib/api/helpers/sse_helpers.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module SSEHelpers
+ def request_from_sse?(project)
+ return false if request.referer.blank?
+
+ uri = URI.parse(request.referer)
+ uri.path.starts_with?(::Gitlab::Routing.url_helpers.project_root_sse_path(project))
+ rescue URI::InvalidURIError
+ false
+ end
+ end
+ end
+end
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index 61ef1d5bde0..332f2f1986f 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -300,7 +300,7 @@ module API
post '/two_factor_otp_check', feature_category: :authentication_and_authorization do
status 200
- break { success: false } unless Feature.enabled?(:two_factor_for_cli)
+ break { success: false, message: 'Feature flag is disabled' } unless Feature.enabled?(:two_factor_for_cli)
actor.update_last_used_at!
user = actor.user
@@ -316,6 +316,8 @@ module API
otp_validation_result = ::Users::ValidateOtpService.new(user).execute(params.fetch(:otp_attempt))
if otp_validation_result[:status] == :success
+ ::Gitlab::Auth::Otp::SessionEnforcer.new(actor.key).update_session
+
{ success: true }
else
{ success: false, message: 'Invalid OTP' }
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index d4690709de4..73723a96401 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -85,9 +85,7 @@ module API
get '/project_info' do
project = find_project(params[:id])
- # TODO sort out authorization for real
- # https://gitlab.com/gitlab-org/gitlab/-/issues/220912
- unless Ability.allowed?(nil, :download_code, project)
+ unless Guest.can?(:download_code, project) || agent.has_access_to?(project)
not_found!
end
@@ -123,3 +121,5 @@ module API
end
end
end
+
+API::Internal::Kubernetes.prepend_if_ee('EE::API::Internal::Kubernetes')
diff --git a/lib/api/internal/pages.rb b/lib/api/internal/pages.rb
index 690f52d89f3..8eaeeae26c2 100644
--- a/lib/api/internal/pages.rb
+++ b/lib/api/internal/pages.rb
@@ -32,26 +32,29 @@ module API
requires :host, type: String, desc: 'The host to query for'
end
get "/" do
- serverless_domain_finder = ServerlessDomainFinder.new(params[:host])
- if serverless_domain_finder.serverless?
- # Handle Serverless domains
- serverless_domain = serverless_domain_finder.execute
- no_content! unless serverless_domain
-
- virtual_domain = Serverless::VirtualDomain.new(serverless_domain)
- no_content! unless virtual_domain
-
- present virtual_domain, with: Entities::Internal::Serverless::VirtualDomain
- else
- # Handle Pages domains
- host = Namespace.find_by_pages_host(params[:host]) || PagesDomain.find_by_domain_case_insensitive(params[:host])
- no_content! unless host
-
- virtual_domain = host.pages_virtual_domain
- no_content! unless virtual_domain
-
- present virtual_domain, with: Entities::Internal::Pages::VirtualDomain
- end
+ ##
+ # Serverless domain proxy has been deprecated and disabled as per
+ # https://gitlab.com/gitlab-org/gitlab-pages/-/issues/467
+ #
+ # serverless_domain_finder = ServerlessDomainFinder.new(params[:host])
+ # if serverless_domain_finder.serverless?
+ # # Handle Serverless domains
+ # serverless_domain = serverless_domain_finder.execute
+ # no_content! unless serverless_domain
+ #
+ # virtual_domain = Serverless::VirtualDomain.new(serverless_domain)
+ # no_content! unless virtual_domain
+ #
+ # present virtual_domain, with: Entities::Internal::Serverless::VirtualDomain
+ # end
+
+ host = Namespace.find_by_pages_host(params[:host]) || PagesDomain.find_by_domain_case_insensitive(params[:host])
+ no_content! unless host
+
+ virtual_domain = host.pages_virtual_domain
+ no_content! unless virtual_domain
+
+ present virtual_domain, with: Entities::Internal::Pages::VirtualDomain
end
end
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 6a6ee7a4e1c..73e2163248d 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -435,3 +435,5 @@ module API
end
end
end
+
+API::Issues.prepend_if_ee('EE::API::Issues')
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index 51659c2e8a1..44751b3d76c 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -76,6 +76,8 @@ module API
build = find_build!(params[:job_id])
+ authorize_read_build_trace!(build) if build
+
header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
content_type 'text/plain'
env['api.format'] = :binary
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index a8fc277989e..c9f29865664 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -57,7 +57,7 @@ module API
success Entities::ProjectLabel
end
params do
- optional :label_id, type: Integer, desc: 'The id of the label to be updated'
+ optional :label_id, type: Integer, desc: 'The ID of the label to be updated'
optional :name, type: String, desc: 'The name of the label to be updated'
use :project_label_update_params
exactly_one_of :label_id, :name
@@ -71,7 +71,7 @@ module API
success Entities::ProjectLabel
end
params do
- optional :label_id, type: Integer, desc: 'The id of the label to be deleted'
+ optional :label_id, type: Integer, desc: 'The ID of the label to be deleted'
optional :name, type: String, desc: 'The name of the label to be deleted'
exactly_one_of :label_id, :name
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 803de51651a..9bea74e2ce9 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -62,7 +62,7 @@ module API
get ":id/members/:user_id" do
source = find_source(source_type, params[:id])
- members = source.members
+ members = source_members(source)
member = members.find_by!(user_id: params[:user_id])
present_members member
diff --git a/lib/api/merge_request_approvals.rb b/lib/api/merge_request_approvals.rb
index 27ef0b9c7cd..00f42703731 100644
--- a/lib/api/merge_request_approvals.rb
+++ b/lib/api/merge_request_approvals.rb
@@ -4,7 +4,7 @@ module API
class MergeRequestApprovals < ::API::Base
before { authenticate_non_get! }
- feature_category :code_review
+ feature_category :source_code_management
helpers do
params :ee_approval_params do
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index d17e451093b..ab0e9b95e4a 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -11,6 +11,7 @@ module API
feature_category :code_review
helpers Helpers::MergeRequestsHelpers
+ helpers Helpers::SSEHelpers
# EE::API::MergeRequests would override the following helpers
helpers do
@@ -216,6 +217,8 @@ module API
handle_merge_request_errors!(merge_request)
+ Gitlab::UsageDataCounters::EditorUniqueCounter.track_sse_edit_action(author: current_user) if request_from_sse?(user_project)
+
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
diff --git a/lib/api/nuget_packages.rb b/lib/api/nuget_packages.rb
deleted file mode 100644
index 65a85f3c930..00000000000
--- a/lib/api/nuget_packages.rb
+++ /dev/null
@@ -1,247 +0,0 @@
-# frozen_string_literal: true
-
-# NuGet Package Manager Client API
-#
-# These API endpoints are not meant to be consumed directly by users. They are
-# called by the NuGet package manager client when users run commands
-# like `nuget install` or `nuget push`.
-module API
- class NugetPackages < ::API::Base
- helpers ::API::Helpers::PackagesManagerClientsHelpers
- helpers ::API::Helpers::Packages::BasicAuthHelpers
-
- feature_category :package_registry
-
- POSITIVE_INTEGER_REGEX = %r{\A[1-9]\d*\z}.freeze
- NON_NEGATIVE_INTEGER_REGEX = %r{\A0|[1-9]\d*\z}.freeze
-
- PACKAGE_FILENAME = 'package.nupkg'
-
- default_format :json
-
- rescue_from ArgumentError do |e|
- render_api_error!(e.message, 400)
- end
-
- helpers do
- def find_packages
- packages = package_finder.execute
-
- not_found!('Packages') unless packages.exists?
-
- packages
- end
-
- def find_package
- package = package_finder(package_version: params[:package_version]).execute
- .first
-
- not_found!('Package') unless package
-
- package
- end
-
- def package_finder(finder_params = {})
- ::Packages::Nuget::PackageFinder.new(
- authorized_user_project,
- **finder_params.merge(package_name: params[:package_name])
- )
- end
- end
-
- before do
- require_packages_enabled!
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a project', regexp: POSITIVE_INTEGER_REGEX
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before do
- authorized_user_project
- end
-
- namespace ':id/packages/nuget' do
- # https://docs.microsoft.com/en-us/nuget/api/service-index
- desc 'The NuGet Service Index' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get 'index', format: :json do
- authorize_read_package!(authorized_user_project)
-
- track_package_event('cli_metadata', :nuget)
-
- present ::Packages::Nuget::ServiceIndexPresenter.new(authorized_user_project),
- with: ::API::Entities::Nuget::ServiceIndex
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/package-publish-resource
- desc 'The NuGet Package Publish endpoint' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- params do
- requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- put do
- authorize_upload!(authorized_user_project)
- bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
-
- file_params = params.merge(
- file: params[:package],
- file_name: PACKAGE_FILENAME
- )
-
- package = ::Packages::Nuget::CreatePackageService.new(authorized_user_project, current_user)
- .execute
-
- package_file = ::Packages::CreatePackageFileService.new(package, file_params)
- .execute
-
- track_package_event('push_package', :nuget)
-
- ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
-
- created!
- rescue ObjectStorage::RemoteStoreError => e
- Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
-
- forbidden!
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- put 'authorize' do
- authorize_workhorse!(
- subject: authorized_user_project,
- has_length: false,
- maximum_size: authorized_user_project.actual_limits.nuget_max_file_size
- )
- end
-
- params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
- end
- namespace '/metadata/*package_name' do
- before do
- authorize_read_package!(authorized_user_project)
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
- desc 'The NuGet Metadata Service - Package name level' do
- detail 'This feature was introduced in GitLab 12.8'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get 'index', format: :json do
- present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages),
- with: ::API::Entities::Nuget::PackagesMetadata
- end
-
- desc 'The NuGet Metadata Service - Package name and version level' do
- detail 'This feature was introduced in GitLab 12.8'
- end
- params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get '*package_version', format: :json do
- present ::Packages::Nuget::PackageMetadataPresenter.new(find_package),
- with: ::API::Entities::Nuget::PackageMetadata
- end
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
- params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
- end
- namespace '/download/*package_name' do
- before do
- authorize_read_package!(authorized_user_project)
- end
-
- desc 'The NuGet Content Service - index request' do
- detail 'This feature was introduced in GitLab 12.8'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get 'index', format: :json do
- present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages),
- with: ::API::Entities::Nuget::PackagesVersions
- end
-
- desc 'The NuGet Content Service - content request' do
- detail 'This feature was introduced in GitLab 12.8'
- end
- params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
- requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get '*package_version/*package_filename', format: :nupkg do
- filename = "#{params[:package_filename]}.#{params[:format]}"
- package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true)
- .execute
-
- not_found!('Package') unless package_file
-
- track_package_event('pull_package', :nuget)
-
- # nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
- present_carrierwave_file!(package_file.file, supports_direct_download: false)
- end
- end
-
- params do
- requires :q, type: String, desc: 'The search term'
- optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX
- optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX
- optional :prerelease, type: Boolean, desc: 'Include prerelease versions', default: true
- end
- namespace '/query' do
- before do
- authorize_read_package!(authorized_user_project)
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
- desc 'The NuGet Search Service' do
- detail 'This feature was introduced in GitLab 12.8'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get format: :json do
- search_options = {
- include_prerelease_versions: params[:prerelease],
- per_page: params[:take],
- padding: params[:skip]
- }
- search = Packages::Nuget::SearchService
- .new(authorized_user_project, params[:q], search_options)
- .execute
-
- track_package_event('search_package', :nuget)
-
- present ::Packages::Nuget::SearchResultsPresenter.new(search),
- with: ::API::Entities::Nuget::SearchResults
- end
- end
- end
- end
- end
-end
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
new file mode 100644
index 00000000000..b2516cc91f8
--- /dev/null
+++ b/lib/api/nuget_project_packages.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+# NuGet Package Manager Client API
+#
+# These API endpoints are not meant to be consumed directly by users. They are
+# called by the NuGet package manager client when users run commands
+# like `nuget install` or `nuget push`.
+module API
+ class NugetProjectPackages < ::API::Base
+ helpers ::API::Helpers::PackagesManagerClientsHelpers
+ helpers ::API::Helpers::Packages::BasicAuthHelpers
+
+ feature_category :package_registry
+
+ PACKAGE_FILENAME = 'package.nupkg'
+
+ default_format :json
+
+ rescue_from ArgumentError do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ before do
+ require_packages_enabled!
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before do
+ authorized_user_project
+ end
+
+ namespace ':id/packages/nuget' do
+ include ::API::Concerns::Packages::NugetEndpoints
+
+ # https://docs.microsoft.com/en-us/nuget/api/package-publish-resource
+ desc 'The NuGet Package Publish endpoint' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ params do
+ requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ put do
+ authorize_upload!(authorized_user_project)
+ bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
+
+ file_params = params.merge(
+ file: params[:package],
+ file_name: PACKAGE_FILENAME
+ )
+
+ package = ::Packages::Nuget::CreatePackageService.new(
+ authorized_user_project,
+ current_user,
+ declared_params.merge(build: current_authenticated_job)
+ ).execute
+
+ package_file = ::Packages::CreatePackageFileService.new(
+ package,
+ file_params.merge(build: current_authenticated_job)
+ ).execute
+
+ track_package_event('push_package', :nuget, category: 'API::NugetPackages')
+
+ ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
+
+ created!
+ rescue ObjectStorage::RemoteStoreError => e
+ Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
+
+ forbidden!
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ put 'authorize' do
+ authorize_workhorse!(
+ subject: authorized_user_project,
+ has_length: false,
+ maximum_size: authorized_user_project.actual_limits.nuget_max_file_size
+ )
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
+ params do
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+ namespace '/download/*package_name' do
+ before do
+ authorize_read_package!(authorized_user_project)
+ end
+
+ desc 'The NuGet Content Service - index request' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get 'index', format: :json do
+ present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages),
+ with: ::API::Entities::Nuget::PackagesVersions
+ end
+
+ desc 'The NuGet Content Service - content request' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+ params do
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get '*package_version/*package_filename', format: :nupkg do
+ filename = "#{params[:package_filename]}.#{params[:format]}"
+ package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true)
+ .execute
+
+ not_found!('Package') unless package_file
+
+ track_package_event('pull_package', :nuget, category: 'API::NugetPackages')
+
+ # nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
+ present_carrierwave_file!(package_file.file, supports_direct_download: false)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb
index cfb0c5fd705..6785b28ddef 100644
--- a/lib/api/project_clusters.rb
+++ b/lib/api/project_clusters.rb
@@ -83,6 +83,8 @@ module API
optional :environment_scope, type: String, desc: 'The associated environment to the cluster'
optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
+ optional :enabled, type: Boolean, desc: 'Determines if cluster is active or not'
+ optional :managed, type: Boolean, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/api/project_repository_storage_moves.rb b/lib/api/project_repository_storage_moves.rb
index fe6de3ea385..196b7d88500 100644
--- a/lib/api/project_repository_storage_moves.rb
+++ b/lib/api/project_repository_storage_moves.rb
@@ -34,6 +34,22 @@ module API
present storage_move, with: Entities::ProjectRepositoryStorageMove, current_user: current_user
end
+
+ desc 'Schedule bulk project repository storage moves' do
+ detail 'This feature was introduced in GitLab 13.7.'
+ end
+ params do
+ requires :source_storage_name, type: String, desc: 'The source storage shard', values: -> { Gitlab.config.repositories.storages.keys }
+ optional :destination_storage_name, type: String, desc: 'The destination storage shard', values: -> { Gitlab.config.repositories.storages.keys }
+ end
+ post do
+ ::Projects::ScheduleBulkRepositoryShardMovesService.enqueue(
+ declared_params[:source_storage_name],
+ declared_params[:destination_storage_name]
+ )
+
+ accepted!
+ end
end
params do
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index 7104fb8d999..658c6d13847 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -127,7 +127,7 @@ module API
track_package_event('push_package', :pypi)
::Packages::Pypi::CreatePackageService
- .new(authorized_user_project, current_user, declared_params)
+ .new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job))
.execute
created!
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index d3a185a51c8..52c73104bb4 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -57,7 +57,7 @@ module API
end
params do
- requires :link_id, type: String, desc: 'The id of the link'
+ requires :link_id, type: String, desc: 'The ID of the link'
end
resource 'links/:link_id' do
desc 'Get a link detail of a release' do
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index b95856d99d1..b3f09b431b0 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -52,6 +52,7 @@ module API
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
+ optional :disable_feed_token, type: Boolean, desc: 'Disable display of RSS/Atom and Calendar `feed_tokens`'
optional :disabled_oauth_sign_in_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Disable certain OAuth sign-in sources'
optional :domain_denylist_enabled, type: Boolean, desc: 'Enable domain denylist for sign ups'
optional :domain_denylist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
@@ -102,6 +103,11 @@ module API
optional :performance_bar_allowed_group_id, type: String, desc: 'Deprecated: Use :performance_bar_allowed_group_path instead. Path of the group that is allowed to toggle the performance bar.' # support legacy names, can be removed in v6
optional :performance_bar_allowed_group_path, type: String, desc: 'Path of the group that is allowed to toggle the performance bar.'
optional :performance_bar_enabled, type: String, desc: 'Deprecated: Pass `performance_bar_allowed_group_path: nil` instead. Allow enabling the performance.' # support legacy names, can be removed in v6
+ optional :personal_access_token_prefix, type: String, desc: 'Prefix to prepend to all personal access tokens'
+ optional :kroki_enabled, type: Boolean, desc: 'Enable Kroki'
+ given kroki_enabled: ->(val) { val } do
+ requires :kroki_url, type: String, desc: 'The Kroki server URL'
+ end
optional :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML'
given plantuml_enabled: ->(val) { val } do
requires :plantuml_url, type: String, desc: 'The PlantUML server URL'
diff --git a/lib/api/statistics.rb b/lib/api/statistics.rb
index 1814e1a6782..6818c04fd2e 100644
--- a/lib/api/statistics.rb
+++ b/lib/api/statistics.rb
@@ -4,7 +4,7 @@ module API
class Statistics < ::API::Base
before { authenticated_as_admin! }
- feature_category :instance_statistics
+ feature_category :devops_reports
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
MergeRequest, Note, Snippet, Key, Milestone].freeze
diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb
index 7b038ec74bb..cad2f52e951 100644
--- a/lib/api/usage_data.rb
+++ b/lib/api/usage_data.rb
@@ -20,6 +20,18 @@ module API
requires :event, type: String, desc: 'The event name that should be tracked'
end
+ post 'increment_counter' do
+ event_name = params[:event]
+
+ increment_counter(event_name)
+
+ status :ok
+ end
+
+ params do
+ requires :event, type: String, desc: 'The event name that should be tracked'
+ end
+
post 'increment_unique_users' do
event_name = params[:event]
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 501ed629c7e..8b9b82877f7 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -534,6 +534,24 @@ module API
user.activate
end
+
+ desc 'Approve a pending user. Available only for admins.'
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
+ post ':id/approve', feature_category: :authentication_and_authorization do
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless can?(current_user, :read_user, user)
+
+ result = ::Users::ApproveService.new(current_user).execute(user)
+
+ if result[:success]
+ result
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
# rubocop: enable CodeReuse/ActiveRecord
desc 'Deactivate an active user. Available only for admins.'
params do
diff --git a/lib/api/validations/validators/absence.rb b/lib/api/validations/validators/absence.rb
index 1f43f3ab126..7858ce7140b 100644
--- a/lib/api/validations/validators/absence.rb
+++ b/lib/api/validations/validators/absence.rb
@@ -7,7 +7,7 @@ module API
def validate_param!(attr_name, params)
return if params.respond_to?(:key?) && !params.key?(attr_name)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:absence)
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:absence))
end
end
end
diff --git a/lib/api/validations/validators/array_none_any.rb b/lib/api/validations/validators/array_none_any.rb
index 7efb8e6ccee..3732c1f575c 100644
--- a/lib/api/validations/validators/array_none_any.rb
+++ b/lib/api/validations/validators/array_none_any.rb
@@ -10,8 +10,10 @@ module API
return if value.is_a?(Array) ||
[IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be an array, 'None' or 'Any'"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be an array, 'None' or 'Any'"
+ )
end
end
end
diff --git a/lib/api/validations/validators/check_assignees_count.rb b/lib/api/validations/validators/check_assignees_count.rb
index b614058e325..92ada159b46 100644
--- a/lib/api/validations/validators/check_assignees_count.rb
+++ b/lib/api/validations/validators/check_assignees_count.rb
@@ -18,9 +18,10 @@ module API
def validate_param!(attr_name, params)
return if param_allowed?(attr_name, params)
- raise Grape::Exceptions::Validation,
- params: [@scope.full_name(attr_name)],
- message: "allows one value, but found #{params[attr_name].size}: #{params[attr_name].join(", ")}"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "allows one value, but found #{params[attr_name].size}: #{params[attr_name].join(", ")}"
+ )
end
private
diff --git a/lib/api/validations/validators/email_or_email_list.rb b/lib/api/validations/validators/email_or_email_list.rb
index b7f2a0cd443..da665f39130 100644
--- a/lib/api/validations/validators/email_or_email_list.rb
+++ b/lib/api/validations/validators/email_or_email_list.rb
@@ -11,9 +11,10 @@ module API
return if value.split(',').map { |v| ValidateEmail.valid?(v) }.all?
- raise Grape::Exceptions::Validation,
+ raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
message: "contains an invalid email address"
+ )
end
end
end
diff --git a/lib/api/validations/validators/file_path.rb b/lib/api/validations/validators/file_path.rb
index 8a815c3b2b8..a6a3c692fd6 100644
--- a/lib/api/validations/validators/file_path.rb
+++ b/lib/api/validations/validators/file_path.rb
@@ -11,8 +11,10 @@ module API
path = Gitlab::Utils.check_path_traversal!(path)
Gitlab::Utils.check_allowed_absolute_path!(path, path_allowlist)
rescue
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be a valid file path"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be a valid file path"
+ )
end
end
end
diff --git a/lib/api/validations/validators/git_ref.rb b/lib/api/validations/validators/git_ref.rb
index 1dda9d758a7..dcb1db6ca33 100644
--- a/lib/api/validations/validators/git_ref.rb
+++ b/lib/api/validations/validators/git_ref.rb
@@ -17,8 +17,10 @@ module API
return unless invalid_character?(revision)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: 'should be a valid reference path'
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: 'should be a valid reference path'
+ )
end
private
diff --git a/lib/api/validations/validators/git_sha.rb b/lib/api/validations/validators/git_sha.rb
index 657307db1df..665d1878b4c 100644
--- a/lib/api/validations/validators/git_sha.rb
+++ b/lib/api/validations/validators/git_sha.rb
@@ -9,8 +9,10 @@ module API
return if Commit::EXACT_COMMIT_SHA_PATTERN.match?(sha)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be a valid sha"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be a valid sha"
+ )
end
end
end
diff --git a/lib/api/validations/validators/integer_none_any.rb b/lib/api/validations/validators/integer_none_any.rb
index aa8c137a6ab..32ab6e19b98 100644
--- a/lib/api/validations/validators/integer_none_any.rb
+++ b/lib/api/validations/validators/integer_none_any.rb
@@ -3,15 +3,11 @@
module API
module Validations
module Validators
- class IntegerNoneAny < Grape::Validations::Base
- def validate_param!(attr_name, params)
- value = params[attr_name]
+ class IntegerNoneAny < IntegerOrCustomValue
+ private
- return if value.is_a?(Integer) ||
- [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
-
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be an integer, 'None' or 'Any'"
+ def extract_custom_values(_options)
+ [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY]
end
end
end
diff --git a/lib/api/validations/validators/integer_or_custom_value.rb b/lib/api/validations/validators/integer_or_custom_value.rb
new file mode 100644
index 00000000000..d2352495948
--- /dev/null
+++ b/lib/api/validations/validators/integer_or_custom_value.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module API
+ module Validations
+ module Validators
+ class IntegerOrCustomValue < Grape::Validations::Base
+ def initialize(attrs, options, required, scope, **opts)
+ @custom_values = extract_custom_values(options)
+ super
+ end
+
+ def validate_param!(attr_name, params)
+ value = params[attr_name]
+
+ return if value.is_a?(Integer)
+ return if @custom_values.map(&:downcase).include?(value.to_s.downcase)
+
+ valid_options = Gitlab::Utils.to_exclusive_sentence(['an integer'] + @custom_values)
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be #{valid_options}, however got #{value}"
+ )
+ end
+
+ private
+
+ def extract_custom_values(options)
+ options.is_a?(Hash) ? options[:values] : options
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/validations/validators/limit.rb b/lib/api/validations/validators/limit.rb
index 3bb4cee1d75..e8f894849a5 100644
--- a/lib/api/validations/validators/limit.rb
+++ b/lib/api/validations/validators/limit.rb
@@ -9,9 +9,10 @@ module API
return if value.size <= @option
- raise Grape::Exceptions::Validation,
+ raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
message: "#{@scope.full_name(attr_name)} must be less than #{@option} characters"
+ )
end
end
end
diff --git a/lib/api/validations/validators/untrusted_regexp.rb b/lib/api/validations/validators/untrusted_regexp.rb
index ec623684e67..3ddea2bd9de 100644
--- a/lib/api/validations/validators/untrusted_regexp.rb
+++ b/lib/api/validations/validators/untrusted_regexp.rb
@@ -11,7 +11,7 @@ module API
Gitlab::UntrustedRegexp.new(value)
rescue RegexpError => e
message = "is an invalid regexp: #{e.message}"
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
end
end
end