summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /lib/api
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
downloadgitlab-ce-aee0a117a889461ce8ced6fcf73207fe017f1d99.tar.gz
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/admin/plan_limits.rb1
-rw-r--r--lib/api/ci/helpers/runner.rb16
-rw-r--r--lib/api/ci/pipelines.rb2
-rw-r--r--lib/api/ci/runner.rb4
-rw-r--r--lib/api/commit_statuses.rb5
-rw-r--r--lib/api/composer_packages.rb10
-rw-r--r--lib/api/concerns/packages/conan_endpoints.rb17
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb4
-rw-r--r--lib/api/dependency_proxy.rb12
-rw-r--r--lib/api/discussions.rb7
-rw-r--r--lib/api/entities/changelog.rb9
-rw-r--r--lib/api/entities/ci/job_basic.rb1
-rw-r--r--lib/api/entities/ci/pipeline.rb4
-rw-r--r--lib/api/entities/ci/pipeline_basic.rb2
-rw-r--r--lib/api/entities/ci/runner.rb2
-rw-r--r--lib/api/entities/commit_signature.rb4
-rw-r--r--lib/api/entities/issue_basic.rb2
-rw-r--r--lib/api/entities/personal_access_token.rb2
-rw-r--r--lib/api/entities/plan_limit.rb1
-rw-r--r--lib/api/entities/project.rb1
-rw-r--r--lib/api/entities/project_import_failed_relation.rb1
-rw-r--r--lib/api/entities/project_import_status.rb7
-rw-r--r--lib/api/group_export.rb4
-rw-r--r--lib/api/groups.rb24
-rw-r--r--lib/api/helpers/label_helpers.rb6
-rw-r--r--lib/api/helpers/members_helpers.rb3
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb15
-rw-r--r--lib/api/helpers/projects_helpers.rb7
-rw-r--r--lib/api/helpers/rate_limiter.rb27
-rw-r--r--lib/api/invitations.rb1
-rw-r--r--lib/api/issues.rb8
-rw-r--r--lib/api/lint.rb4
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/api/merge_request_diffs.rb2
-rw-r--r--lib/api/merge_requests.rb30
-rw-r--r--lib/api/notes.rb7
-rw-r--r--lib/api/project_export.rb4
-rw-r--r--lib/api/project_import.rb6
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/api/repositories.rb75
-rw-r--r--lib/api/resource_label_events.rb2
-rw-r--r--lib/api/resource_milestone_events.rb2
-rw-r--r--lib/api/resource_state_events.rb2
-rw-r--r--lib/api/search.rb4
-rw-r--r--lib/api/settings.rb1
-rw-r--r--lib/api/suggestions.rb4
-rw-r--r--lib/api/terraform/state.rb7
-rw-r--r--lib/api/topics.rb2
-rw-r--r--lib/api/v3/github.rb2
-rw-r--r--lib/api/validations/types/workhorse_file.rb1
50 files changed, 247 insertions, 121 deletions
diff --git a/lib/api/admin/plan_limits.rb b/lib/api/admin/plan_limits.rb
index ab6a4e4a04a..d595b5b2e09 100644
--- a/lib/api/admin/plan_limits.rb
+++ b/lib/api/admin/plan_limits.rb
@@ -37,6 +37,7 @@ module API
optional :conan_max_file_size, type: Integer, desc: 'Maximum Conan package file size in bytes'
optional :generic_packages_max_file_size, type: Integer, desc: 'Maximum generic package file size in bytes'
+ optional :helm_max_file_size, type: Integer, desc: 'Maximum Helm chart file size in bytes'
optional :maven_max_file_size, type: Integer, desc: 'Maximum Maven package file size in bytes'
optional :npm_max_file_size, type: Integer, desc: 'Maximum NPM package file size in bytes'
optional :nuget_max_file_size, type: Integer, desc: 'Maximum NuGet package file size in bytes'
diff --git a/lib/api/ci/helpers/runner.rb b/lib/api/ci/helpers/runner.rb
index dabb6c7ab3a..72c388160b4 100644
--- a/lib/api/ci/helpers/runner.rb
+++ b/lib/api/ci/helpers/runner.rb
@@ -29,7 +29,7 @@ module API
def get_runner_details_from_request
return get_runner_ip unless params['info'].present?
- attributes_for_keys(%w(name version revision platform architecture), params['info'])
+ attributes_for_keys(%w(name version revision platform architecture executor), params['info'])
.merge(get_runner_config_from_request)
.merge(get_runner_ip)
end
@@ -52,7 +52,7 @@ module API
# HTTP status codes to terminate the job on GitLab Runner:
# - 403
- def authenticate_job!(require_running: true)
+ def authenticate_job!(require_running: true, heartbeat_runner: false)
job = current_job
# 404 is not returned here because we want to terminate the job if it's
@@ -70,7 +70,17 @@ module API
job_forbidden!(job, 'Job is not running') unless job.running?
end
- job.runner&.heartbeat(get_runner_ip)
+ # Only some requests (like updating the job or patching the trace) should trigger
+ # runner heartbeat. Operations like artifacts uploading are executed in context of
+ # the running job and in the job environment, which in many cases will cause the IP
+ # to be updated to not the expected value. And operations like artifacts downloads can
+ # be done even after the job is finished and from totally different runners - while
+ # they would then update the connection status of not the runner that they should.
+ # Runner requests done in context of job authentication should explicitly define when
+ # the heartbeat should be triggered.
+ if heartbeat_runner
+ job.runner&.heartbeat(get_runner_ip)
+ end
job
end
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index 03b59e7e6ad..4e5d6c264bf 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -166,7 +166,7 @@ module API
params do
requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
end
- get ':id/pipelines/:pipeline_id/variables', feature_category: :pipeline_authoring do
+ get ':id/pipelines/:pipeline_id/variables', feature_category: :pipeline_authoring, urgency: :low do
authorize! :read_pipeline_variable, pipeline
present pipeline.variables, with: Entities::Ci::Variable
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index aabcf34952c..4317789f7aa 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -176,7 +176,7 @@ module API
optional :exit_code, type: Integer, desc: %q(Job's exit code)
end
put '/:id', feature_category: :continuous_integration do
- job = authenticate_job!
+ job = authenticate_job!(heartbeat_runner: true)
Gitlab::Metrics.add_event(:update_build)
@@ -203,7 +203,7 @@ module API
optional :token, type: String, desc: %q(Job's authentication token)
end
patch '/:id/trace', feature_category: :continuous_integration do
- job = authenticate_job!
+ job = authenticate_job!(heartbeat_runner: true)
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
content_range = request.headers['Content-Range']
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 1785362656e..c89abf72e2d 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -78,13 +78,16 @@ module API
name = params[:name] || params[:context] || 'default'
- pipeline ||= user_project.ci_pipelines.create!(
+ pipeline ||= user_project.ci_pipelines.build(
source: :external,
sha: commit.sha,
ref: ref,
user: current_user,
protected: user_project.protected_for?(ref))
+ pipeline.ensure_project_iid!
+ pipeline.save!
+
authorize! :update_pipeline, pipeline
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index 94cad7e6c65..0e6e04d2645 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -70,7 +70,7 @@ module API
end
desc 'Composer packages endpoint at group level'
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/packages' do
presenter.root
end
@@ -79,7 +79,7 @@ module API
params do
requires :sha, type: String, desc: 'Shasum of current json'
end
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/p/:sha' do
presenter.provider
end
@@ -88,7 +88,7 @@ module API
params do
requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
end
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/p2/*package_name', requirements: COMPOSER_ENDPOINT_REQUIREMENTS, file_path: true do
not_found! if packages.empty?
@@ -99,7 +99,7 @@ module API
params do
requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
end
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
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?
@@ -119,7 +119,7 @@ module API
desc 'Composer packages endpoint for registering packages'
namespace ':id/packages/composer' do
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
params do
optional :branch, type: String, desc: 'The name of the branch'
diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb
index 3194cdebde8..edf20b6aebe 100644
--- a/lib/api/concerns/packages/conan_endpoints.rb
+++ b/lib/api/concerns/packages/conan_endpoints.rb
@@ -27,6 +27,7 @@ module API
PACKAGE_COMPONENT_REGEX = Gitlab::Regex.conan_recipe_component_regex
CONAN_REVISION_REGEX = Gitlab::Regex.conan_revision_regex
+ CONAN_REVISION_USER_CHANNEL_REGEX = Gitlab::Regex.conan_recipe_user_channel_regex
CONAN_FILES = (Gitlab::Regex::Packages::CONAN_RECIPE_FILES + Gitlab::Regex::Packages::CONAN_PACKAGE_FILES).freeze
@@ -105,10 +106,14 @@ module API
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 :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username'
+ requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel'
end
namespace 'conans/:package_name/:package_version/:package_username/:package_channel', requirements: PACKAGE_REQUIREMENTS do
+ after_validation do
+ check_username_channel
+ end
+
# Get the snapshot
#
# the snapshot is a hash of { filename: md5 hash }
@@ -264,8 +269,8 @@ module API
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 :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username'
+ requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_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
@@ -273,6 +278,10 @@ module API
authenticate_non_get!
end
+ after_validation do
+ check_username_channel
+ end
+
params do
requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
end
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index 7a657be5bf3..d6e006df976 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -121,9 +121,7 @@ module API
not_found!('Packages') if packages.empty?
- include_metadata = Feature.enabled?(:packages_npm_abbreviated_metadata, project, default_enabled: :yaml)
-
- present ::Packages::Npm::PackagePresenter.new(package_name, packages, include_metadata: include_metadata),
+ present ::Packages::Npm::PackagePresenter.new(package_name, packages),
with: ::API::Entities::NpmPackage
end
end
diff --git a/lib/api/dependency_proxy.rb b/lib/api/dependency_proxy.rb
index 185b8d5a15d..9d0b1bf4423 100644
--- a/lib/api/dependency_proxy.rb
+++ b/lib/api/dependency_proxy.rb
@@ -6,15 +6,6 @@ module API
feature_category :dependency_proxy
- helpers do
- def obtain_new_purge_cache_lease
- Gitlab::ExclusiveLease
- .new("dependency_proxy:delete_group_blobs:#{user_group.id}",
- timeout: 1.hour)
- .try_obtain
- end
- end
-
after_validation do
authorize! :admin_group, user_group
end
@@ -29,9 +20,6 @@ module API
delete ':id/dependency_proxy/cache' do
not_found! unless user_group.dependency_proxy_feature_available?
- message = 'This request has already been made. It may take some time to purge the cache. You can run this at most once an hour for a given group'
- render_api_error!(message, 409) unless obtain_new_purge_cache_lease
-
# rubocop:disable CodeReuse/Worker
PurgeDependencyProxyCacheWorker.perform_async(current_user.id, user_group.id)
# rubocop:enable CodeReuse/Worker
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index cf4b2348458..0709a8c2036 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -8,6 +8,13 @@ module API
before { authenticate! }
+ urgency :low, [
+ '/projects/:id/merge_requests/:noteable_id/discussions',
+ '/projects/:id/merge_requests/:noteable_id/discussions/:discussion_id',
+ '/projects/:id/merge_requests/:noteable_id/discussions/:discussion_id/notes',
+ '/projects/:id/merge_requests/:noteable_id/discussions/:discussion_id/notes/:note_id'
+ ]
+
Helpers::DiscussionsHelpers.feature_category_per_noteable_type.each do |noteable_type, feature_category|
parent_type = noteable_type.parent_class.to_s.underscore
noteables_str = noteable_type.to_s.underscore.pluralize
diff --git a/lib/api/entities/changelog.rb b/lib/api/entities/changelog.rb
new file mode 100644
index 00000000000..f8ca5826418
--- /dev/null
+++ b/lib/api/entities/changelog.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class Changelog < Grape::Entity
+ expose :to_s, as: :notes
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_basic.rb b/lib/api/entities/ci/job_basic.rb
index c31340f1ff0..0badde4089e 100644
--- a/lib/api/entities/ci/job_basic.rb
+++ b/lib/api/entities/ci/job_basic.rb
@@ -13,6 +13,7 @@ module API
expose :user, with: ::API::Entities::User
expose :commit, with: ::API::Entities::Commit
expose :pipeline, with: ::API::Entities::Ci::PipelineBasic
+ expose :failure_reason, if: -> (job) { job.failed? }
expose :web_url do |job, _options|
Gitlab::Routing.url_helpers.project_job_url(job.project, job)
diff --git a/lib/api/entities/ci/pipeline.rb b/lib/api/entities/ci/pipeline.rb
index 11336ae070d..a8033a21044 100644
--- a/lib/api/entities/ci/pipeline.rb
+++ b/lib/api/entities/ci/pipeline.rb
@@ -10,7 +10,9 @@ module API
expose :created_at, :updated_at, :started_at, :finished_at, :committed_at
expose :duration
expose :queued_duration
- expose :coverage
+ expose :coverage do |pipeline|
+ pipeline.present.coverage
+ end
expose :detailed_status, using: DetailedStatusEntity do |pipeline, options|
pipeline.detailed_status(options[:current_user])
end
diff --git a/lib/api/entities/ci/pipeline_basic.rb b/lib/api/entities/ci/pipeline_basic.rb
index 4d56176bdb3..a2a5a98920a 100644
--- a/lib/api/entities/ci/pipeline_basic.rb
+++ b/lib/api/entities/ci/pipeline_basic.rb
@@ -4,7 +4,7 @@ module API
module Entities
module Ci
class PipelineBasic < Grape::Entity
- expose :id, :project_id, :sha, :ref, :status, :source
+ expose :id, :iid, :project_id, :sha, :ref, :status, :source
expose :created_at, :updated_at
expose :web_url do |pipeline, _options|
diff --git a/lib/api/entities/ci/runner.rb b/lib/api/entities/ci/runner.rb
index 60193fe1df4..c17ff513479 100644
--- a/lib/api/entities/ci/runner.rb
+++ b/lib/api/entities/ci/runner.rb
@@ -14,7 +14,7 @@ module API
expose :online?, as: :online
# DEPRECATED
# TODO Remove in %15.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
- expose :status, as: :deprecated_rest_status
+ expose :deprecated_rest_status, as: :status
end
end
end
diff --git a/lib/api/entities/commit_signature.rb b/lib/api/entities/commit_signature.rb
index 505ce462edf..0d8e977a9f5 100644
--- a/lib/api/entities/commit_signature.rb
+++ b/lib/api/entities/commit_signature.rb
@@ -6,9 +6,9 @@ module API
expose :signature_type
expose :signature, merge: true do |commit, options|
- if commit.signature.is_a?(GpgSignature) || commit.raw_commit_from_rugged?
+ if commit.signature.is_a?(::CommitSignatures::GpgSignature) || commit.raw_commit_from_rugged?
::API::Entities::GpgCommitSignature.represent commit_signature(commit), options
- elsif commit.signature.is_a?(X509CommitSignature)
+ elsif commit.signature.is_a?(::CommitSignatures::X509CommitSignature)
::API::Entities::X509Signature.represent commit.signature, options
end
end
diff --git a/lib/api/entities/issue_basic.rb b/lib/api/entities/issue_basic.rb
index ab248523028..6125dc05a6e 100644
--- a/lib/api/entities/issue_basic.rb
+++ b/lib/api/entities/issue_basic.rb
@@ -23,7 +23,7 @@ module API
expose :issue_type,
as: :type,
format_with: :upcase,
- documentation: { type: "String", desc: "One of #{::WorkItem::Type.base_types.keys.map(&:upcase)}" }
+ documentation: { type: "String", desc: "One of #{::WorkItem::Type.allowed_types_for_issues.map(&:upcase)}" }
expose :assignee, using: ::API::Entities::UserBasic do |issue|
issue.assignees.first
diff --git a/lib/api/entities/personal_access_token.rb b/lib/api/entities/personal_access_token.rb
index 3846929c903..55764daef9d 100644
--- a/lib/api/entities/personal_access_token.rb
+++ b/lib/api/entities/personal_access_token.rb
@@ -3,7 +3,7 @@
module API
module Entities
class PersonalAccessToken < Grape::Entity
- expose :id, :name, :revoked, :created_at, :scopes, :user_id
+ expose :id, :name, :revoked, :created_at, :scopes, :user_id, :last_used_at
expose :active?, as: :active
expose :expires_at do |personal_access_token|
personal_access_token.expires_at ? personal_access_token.expires_at.strftime("%Y-%m-%d") : nil
diff --git a/lib/api/entities/plan_limit.rb b/lib/api/entities/plan_limit.rb
index 04ec44b5167..9f4d1635998 100644
--- a/lib/api/entities/plan_limit.rb
+++ b/lib/api/entities/plan_limit.rb
@@ -5,6 +5,7 @@ module API
class PlanLimit < Grape::Entity
expose :conan_max_file_size
expose :generic_packages_max_file_size
+ expose :helm_max_file_size
expose :maven_max_file_size
expose :npm_max_file_size
expose :nuget_max_file_size
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 662ca59852e..1b9299ed17e 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -117,6 +117,7 @@ module API
expose :squash_option
expose :suggestion_commit_message
expose :merge_commit_template
+ expose :squash_commit_template
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
}
diff --git a/lib/api/entities/project_import_failed_relation.rb b/lib/api/entities/project_import_failed_relation.rb
index b8f842c1646..26cfae7260c 100644
--- a/lib/api/entities/project_import_failed_relation.rb
+++ b/lib/api/entities/project_import_failed_relation.rb
@@ -10,6 +10,7 @@ module API
end
expose :relation_key, as: :relation_name
+ expose :relation_index, as: :line_number
end
end
end
diff --git a/lib/api/entities/project_import_status.rb b/lib/api/entities/project_import_status.rb
index e79c1cdf1a2..5daae4a70f2 100644
--- a/lib/api/entities/project_import_status.rb
+++ b/lib/api/entities/project_import_status.rb
@@ -4,6 +4,7 @@ module API
module Entities
class ProjectImportStatus < ProjectIdentity
expose :import_status
+ expose :import_type
expose :correlation_id do |project, _options|
project.import_state&.correlation_id
end
@@ -15,6 +16,12 @@ module API
expose :import_error do |project, _options|
project.import_state&.last_error
end
+
+ expose :stats do |project, _options|
+ if project.github_import?
+ ::Gitlab::GithubImport::ObjectCounter.summary(project)
+ end
+ end
end
end
end
diff --git a/lib/api/group_export.rb b/lib/api/group_export.rb
index 25cc4e53bd2..f0c0182a02f 100644
--- a/lib/api/group_export.rb
+++ b/lib/api/group_export.rb
@@ -18,7 +18,7 @@ module API
detail 'This feature was introduced in GitLab 12.5.'
end
get ':id/export/download' do
- check_rate_limit! :group_download_export, [current_user, user_group]
+ check_rate_limit! :group_download_export, scope: [current_user, user_group]
if user_group.export_file_exists?
if user_group.export_archive_exists?
@@ -35,7 +35,7 @@ module API
detail 'This feature was introduced in GitLab 12.5.'
end
post ':id/export' do
- check_rate_limit! :group_export, [current_user]
+ check_rate_limit! :group_export, scope: current_user
export_service = ::Groups::ImportExport::ExportService.new(group: user_group, user: current_user)
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 680e3a6e994..d3d1f03585b 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -109,7 +109,7 @@ module API
end
def present_groups_with_pagination_strategies(params, groups)
- return present_groups(params, groups) if current_user.present? || Feature.disabled?(:keyset_pagination_for_groups_api)
+ return present_groups(params, groups) if current_user.present?
options = {
with: Entities::Group,
@@ -382,6 +382,28 @@ module API
end
end
+ desc 'Transfer a group to a new parent group or promote a subgroup to a root group'
+ params do
+ optional :group_id, type: Integer,
+ desc: 'The ID of the target group to which the group needs to be transferred to.'\
+ 'If not provided, the source group will be promoted to a root group.'
+ end
+ post ':id/transfer' do
+ group = find_group!(params[:id])
+ authorize! :admin_group, group
+
+ new_parent_group = find_group!(params[:group_id]) if params[:group_id].present?
+
+ service = ::Groups::TransferService.new(group, current_user)
+
+ if service.execute(new_parent_group)
+ group.preload_shared_group_links
+ present group, with: Entities::GroupDetail, current_user: current_user
+ else
+ render_api_error!(service.error, 400)
+ end
+ end
+
desc 'Share a group with a group' do
success Entities::GroupDetail
end
diff --git a/lib/api/helpers/label_helpers.rb b/lib/api/helpers/label_helpers.rb
index da0ee8f207e..02613cbf9b9 100644
--- a/lib/api/helpers/label_helpers.rb
+++ b/lib/api/helpers/label_helpers.rb
@@ -105,7 +105,11 @@ module API
end
def promote_label(parent)
- authorize! :admin_label, parent
+ unless parent.group
+ render_api_error!('Failed to promote project label to group label', 400)
+ end
+
+ authorize! :admin_label, parent.group
label = find_label(parent, params[:name], include_ancestor_groups: false)
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index 1e89f9f97a2..c2710be6c03 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -8,6 +8,9 @@ module API
params :optional_filter_params_ee do
end
+ params :optional_state_filter_ee do
+ end
+
def find_source(source_type, id)
public_send("find_#{source_type}!", id) # rubocop:disable GitlabSecurity/PublicSend
end
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index 4b6dac39348..031c29e7472 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -7,6 +7,21 @@ module API
module ApiHelpers
include Gitlab::Utils::StrongMemoize
+ def check_username_channel
+ username = declared(params)[:package_username]
+ channel = declared(params)[:package_channel]
+
+ if username == ::Packages::Conan::Metadatum::NONE_VALUE && package_scope == :instance
+ # at the instance level, username must not be empty (naming convention)
+ # don't try to process the empty username and eagerly return not found.
+ not_found!
+ end
+
+ ::Packages::Conan::Metadatum.validate_username_and_channel(username, channel) do |none_field|
+ bad_request!("#{none_field} can't be solely blank")
+ end
+ end
+
def present_download_urls(entity)
authorize!(:read_package, project)
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 42d1c40dd11..d7de8bd8b8b 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -62,6 +62,7 @@ module API
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions'
optional :merge_commit_template, type: String, desc: 'Template used to create merge commit message'
+ optional :squash_commit_template, type: String, desc: 'Template used to create squash commit message'
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled'
@@ -162,6 +163,7 @@ module API
:avatar,
:suggestion_commit_message,
:merge_commit_template,
+ :squash_commit_template,
:repository_storage,
:compliance_framework_setting,
:packages_enabled,
@@ -181,9 +183,10 @@ module API
def filter_attributes_using_license!(attrs)
end
- def validate_git_import_url!(import_url, import_enabled: true)
+ def validate_git_import_url!(import_url)
return if import_url.blank?
- return unless import_enabled
+
+ yield if block_given?
result = Import::ValidateRemoteGitEndpointService.new(url: import_url).execute # network call
diff --git a/lib/api/helpers/rate_limiter.rb b/lib/api/helpers/rate_limiter.rb
index 3a16aef6a74..7d87c74097d 100644
--- a/lib/api/helpers/rate_limiter.rb
+++ b/lib/api/helpers/rate_limiter.rb
@@ -2,26 +2,27 @@
module API
module Helpers
+ # == RateLimiter
+ #
+ # Helper that checks if the rate limit for a given endpoint is throttled by calling the
+ # Gitlab::ApplicationRateLimiter class. If the action is throttled for the current user, the request
+ # will be logged and an error message will be rendered with a Too Many Requests response status.
+ # See app/controllers/concerns/check_rate_limit.rb for Rails controllers version
module RateLimiter
- def check_rate_limit!(key, scope, users_allowlist = nil)
- if rate_limiter.throttled?(key, scope: scope, users_allowlist: users_allowlist)
- log_request(key)
- render_exceeded_limit_error!
- end
- end
+ def check_rate_limit!(key, scope:, **options)
+ return unless rate_limiter.throttled?(key, scope: scope, **options)
- private
+ rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
- def rate_limiter
- ::Gitlab::ApplicationRateLimiter
- end
+ return yield if block_given?
- def render_exceeded_limit_error!
render_api_error!({ error: _('This endpoint has been requested too many times. Try again later.') }, 429)
end
- def log_request(key)
- rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
+ private
+
+ def rate_limiter
+ ::Gitlab::ApplicationRateLimiter
end
end
end
diff --git a/lib/api/invitations.rb b/lib/api/invitations.rb
index f7f5af07378..d78576b5d5b 100644
--- a/lib/api/invitations.rb
+++ b/lib/api/invitations.rb
@@ -24,7 +24,6 @@ module API
requires :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level)'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'invitations-api'
- optional :areas_of_focus, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Areas the inviter wants the member to focus upon'
optional :tasks_to_be_done, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Tasks the inviter wants the member to do'
optional :tasks_project_id, type: Integer, desc: 'The project ID in which to create the task issues'
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 9958526fa7f..4d67cbd1272 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -82,7 +82,7 @@ module API
desc: 'Return issues sorted in `asc` or `desc` order.'
optional :due_date, type: String, values: %w[0 overdue week month next_month_and_previous_two_weeks] << '',
desc: 'Return issues that have no due date (`0`), or whose due date is this week, this month, between two weeks ago and next month, or which are overdue. Accepts: `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`, `0`'
- optional :issue_type, type: String, values: WorkItem::Type.base_types.keys, desc: "The type of the issue. Accepts: #{WorkItem::Type.base_types.keys.join(', ')}"
+ optional :issue_type, type: String, values: WorkItem::Type.allowed_types_for_issues, desc: "The type of the issue. Accepts: #{WorkItem::Type.allowed_types_for_issues.join(', ')}"
use :issues_stats_params
use :pagination
@@ -99,7 +99,7 @@ module API
optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked"
- optional :issue_type, type: String, values: WorkItem::Type.base_types.keys, desc: "The type of the issue. Accepts: #{WorkItem::Type.base_types.keys.join(', ')}"
+ optional :issue_type, type: String, values: WorkItem::Type.allowed_types_for_issues, desc: "The type of the issue. Accepts: #{WorkItem::Type.allowed_types_for_issues.join(', ')}"
use :optional_issue_params_ee
end
@@ -262,7 +262,7 @@ module API
post ':id/issues' do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/21140')
- check_rate_limit! :issues_create, [current_user] if Feature.disabled?("rate_limited_service_issues_create", user_project, default_enabled: :yaml)
+ check_rate_limit!(:issues_create, scope: current_user) if Feature.disabled?("rate_limited_service_issues_create", user_project, default_enabled: :yaml)
authorize! :create_issue, user_project
@@ -472,7 +472,7 @@ module API
end
get ':id/issues/:issue_iid/participants' do
issue = find_project_issue(params[:issue_iid])
- participants = ::Kaminari.paginate_array(issue.participants)
+ participants = ::Kaminari.paginate_array(issue.visible_participants(current_user))
present paginate(participants), with: Entities::UserBasic, current_user: current_user, project: user_project
end
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index 3655cb56564..bfd457a3092 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -43,7 +43,7 @@ module API
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
optional :include_jobs, type: Boolean, desc: 'Whether or not to include CI jobs in the response'
end
- get ':id/ci/lint' do
+ get ':id/ci/lint', urgency: :low do
authorize! :download_code, user_project
content = user_project.repository.gitlab_ci_yml_for(user_project.commit.id, user_project.ci_config_path_or_default)
@@ -64,7 +64,7 @@ module API
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
optional :include_jobs, type: Boolean, desc: 'Whether or not to include CI jobs in the response'
end
- post ':id/ci/lint' do
+ post ':id/ci/lint', urgency: :low do
authorize! :create_pipeline, user_project
result = Gitlab::Ci::Lint
diff --git a/lib/api/members.rb b/lib/api/members.rb
index f488c8c26fc..4798edc4ddf 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -41,6 +41,7 @@ module API
optional :query, type: String, desc: 'A query string to search for members'
optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to look up for membership'
optional :show_seat_info, type: Boolean, desc: 'Show seat information for members'
+ use :optional_state_filter_ee
use :pagination
end
@@ -94,7 +95,6 @@ module API
requires :user_id, types: [Integer, String], desc: 'The user ID of the new member or multiple IDs separated by commas.'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'members-api'
- optional :areas_of_focus, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Areas the inviter wants the member to focus upon'
optional :tasks_to_be_done, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Tasks the inviter wants the member to do'
optional :tasks_project_id, type: Integer, desc: 'The project ID in which to create the task issues'
end
diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb
index 8fa7138af42..87623568a04 100644
--- a/lib/api/merge_request_diffs.rb
+++ b/lib/api/merge_request_diffs.rb
@@ -38,7 +38,7 @@ module API
requires :version_id, type: Integer, desc: 'The ID of a merge request diff version'
end
- get ":id/merge_requests/:merge_request_iid/versions/:version_id" do
+ get ":id/merge_requests/:merge_request_iid/versions/:version_id", urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present_cached merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull, cache_context: nil
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 96d1a69c03a..3f39af7f909 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -134,7 +134,7 @@ module API
use :merge_requests_params
use :optional_scope_param
end
- get feature_category: :code_review do
+ get feature_category: :code_review, urgency: :low do
authenticate! unless params[:scope] == 'all'
validate_anonymous_search_access! if params[:search].present?
merge_requests = find_merge_requests
@@ -155,7 +155,7 @@ module API
optional :non_archived, type: Boolean, desc: 'Return merge requests from non archived projects',
default: true
end
- get ":id/merge_requests", feature_category: :code_review do
+ get ":id/merge_requests", feature_category: :code_review, urgency: :low do
validate_anonymous_search_access! if declared_params[:search].present?
merge_requests = find_merge_requests(group_id: user_group.id, include_subgroups: true)
@@ -195,7 +195,7 @@ module API
use :merge_requests_params
optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IID array of merge requests'
end
- get ":id/merge_requests", feature_category: :code_review do
+ get ":id/merge_requests", feature_category: :code_review, urgency: :low do
authorize! :read_merge_request, user_project
validate_anonymous_search_access! if declared_params[:search].present?
@@ -222,7 +222,7 @@ module API
desc: 'The target project of the merge request defaults to the :id of the project'
use :optional_params
end
- post ":id/merge_requests", feature_category: :code_review do
+ post ":id/merge_requests", feature_category: :code_review, urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20770')
authorize! :create_merge_request_from, user_project
@@ -244,7 +244,7 @@ module API
params do
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
end
- delete ":id/merge_requests/:merge_request_iid", feature_category: :code_review do
+ delete ":id/merge_requests/:merge_request_iid", feature_category: :code_review, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
authorize!(:destroy_merge_request, merge_request)
@@ -263,7 +263,7 @@ module API
desc 'Get a single merge request' do
success Entities::MergeRequest
end
- get ':id/merge_requests/:merge_request_iid', feature_category: :code_review do
+ get ':id/merge_requests/:merge_request_iid', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request,
@@ -279,10 +279,10 @@ module API
desc 'Get the participants of a merge request' do
success Entities::UserBasic
end
- get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review do
+ get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- participants = ::Kaminari.paginate_array(merge_request.participants)
+ participants = ::Kaminari.paginate_array(merge_request.visible_participants(current_user))
present paginate(participants), with: Entities::UserBasic
end
@@ -290,7 +290,7 @@ module API
desc 'Get the commits of a merge request' do
success Entities::Commit
end
- get ':id/merge_requests/:merge_request_iid/commits', feature_category: :code_review do
+ get ':id/merge_requests/:merge_request_iid/commits', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
commits =
@@ -303,7 +303,7 @@ module API
desc 'Get the context commits of a merge request' do
success Entities::Commit
end
- get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
+ get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review, urgency: :high do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
project = merge_request.project
@@ -371,7 +371,7 @@ module API
desc 'Show the merge request changes' do
success Entities::MergeRequestChanges
end
- get ':id/merge_requests/:merge_request_iid/changes', feature_category: :code_review do
+ get ':id/merge_requests/:merge_request_iid/changes', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request,
@@ -422,7 +422,7 @@ module API
use :optional_params
at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
end
- put ':id/merge_requests/:merge_request_iid', feature_category: :code_review do
+ put ':id/merge_requests/:merge_request_iid', feature_category: :code_review, urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20772')
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
@@ -454,7 +454,7 @@ module API
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
end
- put ':id/merge_requests/:merge_request_iid/merge', feature_category: :code_review do
+ put ':id/merge_requests/:merge_request_iid/merge', feature_category: :code_review, urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/4796')
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -524,7 +524,7 @@ module API
params do
optional :skip_ci, type: Boolean, desc: 'Do not create CI pipeline'
end
- put ':id/merge_requests/:merge_request_iid/rebase', feature_category: :code_review do
+ put ':id/merge_requests/:merge_request_iid/rebase', feature_category: :code_review, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
authorize_push_to_merge_request!(merge_request)
@@ -543,7 +543,7 @@ module API
params do
use :pagination
end
- get ':id/merge_requests/:merge_request_iid/closes_issues', feature_category: :code_review do
+ get ':id/merge_requests/:merge_request_iid/closes_issues', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
issues = ::Kaminari.paginate_array(merge_request.visible_closing_issues_for(current_user))
issues = paginate(issues)
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 656eaa2b2bb..93ef77d5a62 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -7,6 +7,11 @@ module API
before { authenticate! }
+ urgency :low, [
+ '/projects/:id/merge_requests/:noteable_id/notes',
+ '/projects/:id/merge_requests/:noteable_id/notes/:note_id'
+ ]
+
Helpers::NotesHelpers.feature_category_per_noteable_type.each do |noteable_type, feature_category|
parent_type = noteable_type.parent_class.to_s.underscore
noteables_str = noteable_type.to_s.underscore.pluralize
@@ -74,7 +79,7 @@ module API
post ":id/#{noteables_str}/:noteable_id/notes", feature_category: feature_category do
allowlist =
Gitlab::CurrentSettings.current_application_settings.notes_create_limit_allowlist
- check_rate_limit! :notes_create, [current_user], allowlist
+ check_rate_limit! :notes_create, scope: current_user, users_allowlist: allowlist
noteable = find_noteable(noteable_type, params[:noteable_id])
opts = {
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index e01c195dbc4..843f72c0e1d 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -25,7 +25,7 @@ module API
detail 'This feature was introduced in GitLab 10.6.'
end
get ':id/export/download' do
- check_rate_limit! :project_download_export, [current_user, user_project]
+ check_rate_limit! :project_download_export, scope: [current_user, user_project]
if user_project.export_file_exists?
if user_project.export_archive_exists?
@@ -49,7 +49,7 @@ module API
end
end
post ':id/export' do
- check_rate_limit! :project_export, [current_user]
+ check_rate_limit! :project_export, scope: current_user
user_project.remove_exports
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index e7c532e2483..7bdcaa5a26f 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -81,7 +81,7 @@ module API
post 'import' do
require_gitlab_workhorse!
- check_rate_limit! :project_import, [current_user, :project_import]
+ check_rate_limit! :project_import, scope: [current_user, :project_import]
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/21041')
@@ -107,7 +107,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- desc 'Get a project export status' do
+ desc 'Get a project import status' do
detail 'This feature was introduced in GitLab 10.6.'
success Entities::ProjectImportStatus
end
@@ -135,7 +135,7 @@ module API
post 'remote-import' do
not_found! unless ::Feature.enabled?(:import_project_from_remote_file)
- check_rate_limit! :project_import, [current_user, :project_import]
+ check_rate_limit! :project_import, scope: [current_user, :project_import]
response = ::Import::GitlabProjects::CreateProjectFromRemoteFileService.new(
current_user,
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 9f0077d23d8..67f0b7af7a9 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -270,7 +270,7 @@ module API
attrs = translate_params_for_compatibility(attrs)
filter_attributes_using_license!(attrs)
- validate_git_import_url!(params[:import_url], import_enabled: check_import_by_url_is_enabled)
+ validate_git_import_url!(params[:import_url]) { check_import_by_url_is_enabled }
project = ::Projects::CreateService.new(current_user, attrs).execute
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 2dd0e40afba..fc976c23726 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -10,6 +10,32 @@ module API
helpers ::API::Helpers::HeadersHelpers
+ helpers do
+ params :release_params do
+ requires :version,
+ type: String,
+ regexp: Gitlab::Regex.unbounded_semver_regex,
+ desc: 'The version of the release, using the semantic versioning format'
+
+ optional :from,
+ type: String,
+ desc: 'The first commit in the range of commits to use for the changelog'
+
+ optional :to,
+ type: String,
+ desc: 'The last commit in the range of commits to use for the changelog'
+
+ optional :date,
+ type: DateTime,
+ desc: 'The date and time of the release'
+
+ optional :trailer,
+ type: String,
+ desc: 'The Git trailer to use for determining if commits are to be included in the changelog',
+ default: ::Repositories::ChangelogService::DEFAULT_TRAILER
+ end
+ end
+
before { authorize! :download_code, user_project }
feature_category :source_code_management
@@ -19,7 +45,7 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
helpers do
- include ::Gitlab::RateLimitHelpers
+ include Gitlab::RepositoryArchiveRateLimiter
def handle_project_member_errors(errors)
if errors[:project_access].any?
@@ -124,8 +150,8 @@ module API
optional :path, type: String, desc: 'Subfolder of the repository to be downloaded'
end
get ':id/repository/archive', requirements: { format: Gitlab::PathRegex.archive_formats_regex } do
- if archive_rate_limit_reached?(current_user, user_project)
- render_api_error!({ error: ::Gitlab::RateLimitHelpers::ARCHIVE_RATE_LIMIT_REACHED_MESSAGE }, 429)
+ check_archive_rate_limit!(current_user, user_project) do
+ render_api_error!({ error: _('This archive has been requested too many times. Try again later.') }, 429)
end
not_acceptable! if Gitlab::HotlinkingDetector.intercept_hotlinking?(request)
@@ -208,36 +234,33 @@ module API
end
end
- desc 'Generates a changelog section for a release' do
- detail 'This feature was introduced in GitLab 13.9'
+ desc 'Generates a changelog section for a release and returns it' do
+ detail 'This feature was introduced in GitLab 14.6'
end
params do
- requires :version,
- type: String,
- regexp: Gitlab::Regex.unbounded_semver_regex,
- desc: 'The version of the release, using the semantic versioning format'
-
- optional :from,
- type: String,
- desc: 'The first commit in the range of commits to use for the changelog'
+ use :release_params
+ end
+ get ':id/repository/changelog' do
+ service = ::Repositories::ChangelogService.new(
+ user_project,
+ current_user,
+ **declared_params(include_missing: false)
+ )
+ changelog = service.execute(commit_to_changelog: false)
- optional :to,
- type: String,
- desc: 'The last commit in the range of commits to use for the changelog'
+ present changelog, with: Entities::Changelog
+ end
- optional :date,
- type: DateTime,
- desc: 'The date and time of the release'
+ desc 'Generates a changelog section for a release and commits it in a changelog file' do
+ detail 'This feature was introduced in GitLab 13.9'
+ end
+ params do
+ use :release_params
optional :branch,
type: String,
desc: 'The branch to commit the changelog changes to'
- optional :trailer,
- type: String,
- desc: 'The Git trailer to use for determining if commits are to be included in the changelog',
- default: ::Repositories::ChangelogService::DEFAULT_TRAILER
-
optional :file,
type: String,
desc: 'The file to commit the changelog changes to',
@@ -261,7 +284,7 @@ module API
**declared_params(include_missing: false)
)
- service.execute
+ service.execute(commit_to_changelog: true)
status(200)
rescue Gitlab::Changelog::Error => ex
render_api_error!("Failed to generate the changelog: #{ex.message}", 422)
@@ -269,3 +292,5 @@ module API
end
end
end
+
+API::Repositories.prepend_mod
diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb
index 33589f6c393..cd56809f45a 100644
--- a/lib/api/resource_label_events.rb
+++ b/lib/api/resource_label_events.rb
@@ -24,7 +24,7 @@ module API
use :pagination
end
- get ":id/#{eventables_str}/:eventable_id/resource_label_events", feature_category: feature_category do
+ get ":id/#{eventables_str}/:eventable_id/resource_label_events", feature_category: feature_category, urgency: :low do
eventable = find_noteable(eventable_type, params[:eventable_id])
events = eventable.resource_label_events.inc_relations
diff --git a/lib/api/resource_milestone_events.rb b/lib/api/resource_milestone_events.rb
index c0483ca59c2..04d71faa56a 100644
--- a/lib/api/resource_milestone_events.rb
+++ b/lib/api/resource_milestone_events.rb
@@ -26,7 +26,7 @@ module API
use :pagination
end
- get ":id/#{eventables_str}/:eventable_id/resource_milestone_events", feature_category: feature_category do
+ get ":id/#{eventables_str}/:eventable_id/resource_milestone_events", feature_category: feature_category, urgency: :low do
eventable = find_noteable(eventable_type, params[:eventable_id])
events = ResourceMilestoneEventFinder.new(current_user, eventable).execute
diff --git a/lib/api/resource_state_events.rb b/lib/api/resource_state_events.rb
index 9b6f6a954b4..4b92f320d6f 100644
--- a/lib/api/resource_state_events.rb
+++ b/lib/api/resource_state_events.rb
@@ -25,7 +25,7 @@ module API
use :pagination
end
- get ":id/#{eventable_name.pluralize}/:eventable_iid/resource_state_events", feature_category: feature_category do
+ get ":id/#{eventable_name.pluralize}/:eventable_iid/resource_state_events", feature_category: feature_category, urgency: :low do
eventable = find_noteable(eventable_class, params[:eventable_iid])
events = ResourceStateEventFinder.new(current_user, eventable).execute
diff --git a/lib/api/search.rb b/lib/api/search.rb
index 3c5801366a8..fbdbe3476db 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -8,6 +8,10 @@ module API
feature_category :global_search
+ rescue_from ActiveRecord::QueryCanceled do |e|
+ render_api_error!({ error: 'Request timed out' }, 408)
+ end
+
helpers do
SCOPE_ENTITY = {
merge_requests: Entities::MergeRequestBasic,
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 12e1d21a00d..508ccdb4b33 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -154,7 +154,6 @@ module API
optional :spam_check_endpoint_enabled, type: Boolean, desc: 'Enable Spam Check via external API endpoint'
given spam_check_endpoint_enabled: ->(val) { val } do
requires :spam_check_endpoint_url, type: String, desc: 'The URL of the external Spam Check service endpoint'
- requires :spam_check_api_key, type: String, desc: 'The API key used by GitLab for accessing the Spam Check service endpoint'
end
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
diff --git a/lib/api/suggestions.rb b/lib/api/suggestions.rb
index 7921700e365..0697169b49a 100644
--- a/lib/api/suggestions.rb
+++ b/lib/api/suggestions.rb
@@ -14,7 +14,7 @@ module API
requires :id, type: String, desc: 'The suggestion ID'
optional :commit_message, type: String, desc: "A custom commit message to use instead of the default generated message or the project's default message"
end
- put ':id/apply' do
+ put ':id/apply', urgency: :low do
suggestion = Suggestion.find_by_id(params[:id])
if suggestion
@@ -31,7 +31,7 @@ module API
requires :ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: "An array of suggestion ID's"
optional :commit_message, type: String, desc: "A custom commit message to use instead of the default generated message or the project's default message"
end
- put 'batch_apply' do
+ put 'batch_apply', urgency: :low do
ids = params[:ids]
suggestions = Suggestion.id_in(ids)
diff --git a/lib/api/terraform/state.rb b/lib/api/terraform/state.rb
index f6dfbcafbb6..29e71611092 100644
--- a/lib/api/terraform/state.rb
+++ b/lib/api/terraform/state.rb
@@ -11,6 +11,13 @@ module API
default_format :json
+ rescue_from(
+ ::ActiveRecord::RecordNotUnique,
+ ::PG::UniqueViolation
+ ) do |e|
+ render_api_error!(e.message, 422)
+ end
+
before do
authenticate!
authorize! :read_terraform_state, user_project
diff --git a/lib/api/topics.rb b/lib/api/topics.rb
index bd28ebe58a9..b9c2bcc2da8 100644
--- a/lib/api/topics.rb
+++ b/lib/api/topics.rb
@@ -69,6 +69,8 @@ module API
topic = ::Projects::Topic.find(params[:id])
+ topic.remove_avatar! if params.key?(:avatar) && params[:avatar].nil?
+
if topic.update(declared_params(include_missing: false))
present topic, with: Entities::Projects::Topic
else
diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb
index 677d0840208..d6c026963e1 100644
--- a/lib/api/v3/github.rb
+++ b/lib/api/v3/github.rb
@@ -101,8 +101,6 @@ module API
# of time after a Gitaly timeout, to mitigate frequent Gitaly timeouts
# for some Commit diffs.
def diff_files(commit)
- return commit.diffs.diff_files unless Feature.enabled?(:api_v3_commits_skip_diff_files, commit.project, default_enabled: :yaml)
-
cache_key = [
GITALY_TIMEOUT_CACHE_KEY,
commit.project.id,
diff --git a/lib/api/validations/types/workhorse_file.rb b/lib/api/validations/types/workhorse_file.rb
index e65e94fc8db..23f402596d5 100644
--- a/lib/api/validations/types/workhorse_file.rb
+++ b/lib/api/validations/types/workhorse_file.rb
@@ -5,6 +5,7 @@ module API
module Types
class WorkhorseFile
def self.parse(value)
+ return if value.blank?
raise "#{value.class} is not an UploadedFile type" unless parsed?(value)
value