summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/api.rb4
-rw-r--r--lib/api/appearance.rb3
-rw-r--r--lib/api/branches.rb1
-rw-r--r--lib/api/bulk_imports.rb28
-rw-r--r--lib/api/ci/jobs.rb8
-rw-r--r--lib/api/ci/runner.rb1
-rw-r--r--lib/api/concerns/packages/debian_distribution_endpoints.rb8
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb4
-rw-r--r--lib/api/concerns/packages/nuget_endpoints.rb6
-rw-r--r--lib/api/debian_group_packages.rb6
-rw-r--r--lib/api/debian_project_packages.rb8
-rw-r--r--lib/api/discussions.rb2
-rw-r--r--lib/api/entities/appearance.rb6
-rw-r--r--lib/api/entities/application_setting.rb5
-rw-r--r--lib/api/entities/basic_project_details.rb10
-rw-r--r--lib/api/entities/bulk_imports/entity.rb5
-rw-r--r--lib/api/entities/ml/mlflow/run_info.rb1
-rw-r--r--lib/api/entities/project_integration_basic.rb1
-rw-r--r--lib/api/entities/remote_mirror.rb2
-rw-r--r--lib/api/environments.rb37
-rw-r--r--lib/api/files.rb13
-rw-r--r--lib/api/group_debian_distributions.rb2
-rw-r--r--lib/api/group_export.rb116
-rw-r--r--lib/api/helpers.rb25
-rw-r--r--lib/api/helpers/award_emoji.rb2
-rw-r--r--lib/api/helpers/discussions_helpers.rb4
-rw-r--r--lib/api/helpers/integrations_helpers.rb33
-rw-r--r--lib/api/helpers/members_helpers.rb3
-rw-r--r--lib/api/helpers/notes_helpers.rb2
-rw-r--r--lib/api/helpers/packages_helpers.rb11
-rw-r--r--lib/api/helpers/pagination_strategies.rb7
-rw-r--r--lib/api/helpers/projects_helpers.rb3
-rw-r--r--lib/api/helpers/rate_limiter.rb17
-rw-r--r--lib/api/helpers/remote_mirrors_helpers.rb26
-rw-r--r--lib/api/helpers/resource_events_helpers.rb2
-rw-r--r--lib/api/import_github.rb34
-rw-r--r--lib/api/internal/base.rb12
-rw-r--r--lib/api/invitations.rb2
-rw-r--r--lib/api/issues.rb6
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/api/merge_request_diffs.rb2
-rw-r--r--lib/api/merge_requests.rb65
-rw-r--r--lib/api/ml/mlflow.rb3
-rw-r--r--lib/api/nuget_group_packages.rb4
-rw-r--r--lib/api/nuget_project_packages.rb4
-rw-r--r--lib/api/pages_domains.rb4
-rw-r--r--lib/api/project_debian_distributions.rb6
-rw-r--r--lib/api/project_export.rb167
-rw-r--r--lib/api/projects.rb1
-rw-r--r--lib/api/release/links.rb4
-rw-r--r--lib/api/releases.rb11
-rw-r--r--lib/api/remote_mirrors.rb10
-rw-r--r--lib/api/resource_milestone_events.rb2
-rw-r--r--lib/api/rubygem_packages.rb32
-rw-r--r--lib/api/search.rb9
-rw-r--r--lib/api/settings.rb13
-rw-r--r--lib/api/subscriptions.rb2
-rw-r--r--lib/api/suggestions.rb2
-rw-r--r--lib/api/users.rb62
-rw-r--r--lib/api/validations/validators/bulk_imports.rb49
60 files changed, 618 insertions, 302 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index b23b11d0c29..5e449022676 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -254,6 +254,7 @@ module API
mount ::API::NugetProjectPackages
mount ::API::PackageFiles
mount ::API::Pages
+ mount ::API::PagesDomains
mount ::API::PersonalAccessTokens::SelfInformation
mount ::API::PersonalAccessTokens
mount ::API::ProjectClusters
@@ -296,6 +297,7 @@ module API
mount ::API::UsageData
mount ::API::UsageDataNonSqlMetrics
mount ::API::UsageDataQueries
+ mount ::API::Users
mount ::API::UserCounts
mount ::API::Wikis
@@ -318,7 +320,6 @@ module API
mount ::API::Labels
mount ::API::Notes
mount ::API::NotificationSettings
- mount ::API::PagesDomains
mount ::API::ProjectEvents
mount ::API::ProjectMilestones
mount ::API::ProtectedTags
@@ -333,7 +334,6 @@ module API
mount ::API::Todos
mount ::API::UsageData
mount ::API::UsageDataNonSqlMetrics
- mount ::API::Users
mount ::API::Ml::Mlflow
end
diff --git a/lib/api/appearance.rb b/lib/api/appearance.rb
index 2cef1b27504..99278bdf8b0 100644
--- a/lib/api/appearance.rb
+++ b/lib/api/appearance.rb
@@ -26,10 +26,11 @@ module API
end
params do
optional :title, type: String, desc: 'Instance title on the sign in / sign up page'
- optional :short_title, type: String, desc: 'Short title for Progressive Web App'
+ optional :pwa_short_name, type: String, desc: 'Optional, short name for Progressive Web App'
optional :description, type: String, desc: 'Markdown text shown on the sign in / sign up page'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :logo, type: File, desc: 'Instance image used on the sign in / sign up page' # rubocop:disable Scalability/FileUploads
+ optional :pwa_icon, type: File, desc: 'Icon used for Progressive Web App' # rubocop:disable Scalability/FileUploads
optional :header_logo, type: File, desc: 'Instance image used for the main navigation bar' # rubocop:disable Scalability/FileUploads
optional :favicon, type: File, desc: 'Instance favicon in .ico/.png format' # rubocop:disable Scalability/FileUploads
optional :new_project_guidelines, type: String, desc: 'Markdown text shown on the new project page'
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 845e42c2ed8..5ae1a80a7fd 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -24,6 +24,7 @@ module API
helpers do
params :filter_params do
optional :search, type: String, desc: 'Return list of branches matching the search criteria'
+ optional :regex, type: String, desc: 'Return list of branches matching the regex'
optional :sort, type: String, desc: 'Return list of branches sorted by the given field', values: %w[name_asc updated_asc updated_desc]
end
end
diff --git a/lib/api/bulk_imports.rb b/lib/api/bulk_imports.rb
index a28db321348..6c07b15329e 100644
--- a/lib/api/bulk_imports.rb
+++ b/lib/api/bulk_imports.rb
@@ -33,7 +33,7 @@ module API
end
before do
- not_found! unless ::BulkImports::Features.enabled?
+ not_found! unless Gitlab::CurrentSettings.bulk_import_enabled?
authenticate!
end
@@ -61,12 +61,30 @@ module API
type: String,
desc: 'Source entity type (only `group_entity` is supported)',
values: %w[group_entity]
- requires :source_full_path, type: String, desc: 'Source full path of the entity to import'
- requires :destination_namespace, type: String, desc: 'Destination namespace for the entity'
- optional :destination_slug, type: String, desc: 'Destination slug for the entity'
+ requires :source_full_path,
+ type: String,
+ desc: 'Relative path of the source entity to import',
+ source_full_path: true,
+ documentation: { example: "'source/full/path' not 'https://example.com/source/full/path'" }
+ requires :destination_namespace,
+ type: String,
+ desc: 'Destination namespace for the entity',
+ destination_namespace_path: true,
+ documentation: { example: "'destination_namespace' or 'destination/namespace'" }
+ optional :destination_slug,
+ type: String,
+ desc: 'Destination slug for the entity',
+ destination_slug_path: true,
+ documentation: { example: "'destination_slug' not 'destination/slug'" }
optional :destination_name,
type: String,
- desc: 'Deprecated: Use :destination_slug instead. Destination slug for the entity'
+ desc: 'Deprecated: Use :destination_slug instead. Destination slug for the entity',
+ destination_slug_path: true,
+ documentation: { example: "'destination_slug' not 'destination/slug'" }
+ optional :migrate_projects,
+ type: Boolean,
+ default: true,
+ desc: 'Indicates group migration should include nested projects'
mutually_exclusive :destination_slug, :destination_name
at_least_one_of :destination_slug, :destination_name
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index bb57a717f7c..ed1c7dfbfa2 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -53,11 +53,15 @@ module API
authorize_read_builds!
- builds = user_project.builds.order('id DESC')
+ builds = user_project.builds.order(id: :desc)
builds = filter_builds(builds, params[:scope])
builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project)
- present paginate(builds, without_count: true), with: Entities::Ci::Job
+ if Feature.enabled?(:jobs_api_keyset_pagination, user_project)
+ present paginate_with_strategies(builds, paginator_params: { without_count: true }), with: Entities::Ci::Job
+ else
+ present paginate(builds, without_count: true), with: Entities::Ci::Job
+ end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index b073eb49bf1..6b4394114df 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -312,6 +312,7 @@ module API
optional :artifact_format, type: String, desc: %q(The format of artifact),
default: 'zip', values: ::Ci::JobArtifact.file_formats.keys
optional :metadata, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact metadata to store (generated by Multipart middleware)), documentation: { type: 'file' }
+ optional :accessibility, type: String, desc: %q(Specify accessibility level of artifact private/public)
end
post '/:id/artifacts', feature_category: :build_artifacts, urgency: :low do
not_allowed! unless Gitlab.config.artifacts.enabled
diff --git a/lib/api/concerns/packages/debian_distribution_endpoints.rb b/lib/api/concerns/packages/debian_distribution_endpoints.rb
index 76b996f2301..6fe3f432edb 100644
--- a/lib/api/concerns/packages/debian_distribution_endpoints.rb
+++ b/lib/api/concerns/packages/debian_distribution_endpoints.rb
@@ -80,10 +80,10 @@ module API
use :optional_distribution_params
end
post '/' do
- authorize_create_package!(project_or_group)
+ authorize_create_package!(project_or_group(:read_project))
distribution_params = declared_params(include_missing: false)
- result = ::Packages::Debian::CreateDistributionService.new(project_or_group, current_user, distribution_params).execute
+ result = ::Packages::Debian::CreateDistributionService.new(project_or_group(:read_project), current_user, distribution_params).execute
created_distribution = result.payload[:distribution]
if result.success?
@@ -183,7 +183,7 @@ module API
use :optional_distribution_params
end
put '/:codename' do
- authorize_create_package!(project_or_group)
+ authorize_create_package!(project_or_group(:read_project))
distribution_params = declared_params(include_missing: false).except(:codename)
result = ::Packages::Debian::UpdateDistributionService.new(distribution, distribution_params).execute
@@ -214,7 +214,7 @@ module API
use :optional_distribution_params
end
delete '/:codename' do
- authorize_destroy_package!(project_or_group)
+ authorize_destroy_package!(project_or_group(:read_project))
accepted! if distribution.destroy
diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index 842250d351b..181759a7f38 100644
--- a/lib/api/concerns/packages/debian_package_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -35,10 +35,10 @@ module API
::Packages::Debian::DistributionsFinder.new(container, codename_or_suite: params[:distribution]).execute.last!
end
- def present_distribution_package_file!
+ def present_distribution_package_file!(project)
not_found! unless params[:package_name].start_with?(params[:letter])
- package_file = distribution_from!(user_project).package_files.with_file_name(params[:file_name]).last!
+ package_file = distribution_from!(project).package_files.with_file_name(params[:file_name]).last!
present_package_file!(package_file)
end
diff --git a/lib/api/concerns/packages/nuget_endpoints.rb b/lib/api/concerns/packages/nuget_endpoints.rb
index 31ecb529c3c..5f32f0544f4 100644
--- a/lib/api/concerns/packages/nuget_endpoints.rb
+++ b/lib/api/concerns/packages/nuget_endpoints.rb
@@ -64,7 +64,7 @@ module API
tags %w[nuget_packages]
end
get 'index', format: :json, urgency: :default do
- authorize_read_package!(project_or_group)
+ authorize_packages_access!(project_or_group, required_permission)
track_package_event('cli_metadata', :nuget, **snowplow_gitlab_standard_context.merge(category: 'API::NugetPackages'))
@@ -78,7 +78,7 @@ module API
end
namespace '/metadata/*package_name' do
after_validation do
- authorize_read_package!(project_or_group)
+ authorize_packages_access!(project_or_group, required_permission)
end
desc 'The NuGet Metadata Service - Package name level' do
@@ -124,7 +124,7 @@ module API
end
namespace '/query' do
after_validation do
- authorize_read_package!(project_or_group)
+ authorize_packages_access!(project_or_group, required_permission)
end
desc 'The NuGet Search Service' do
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 105a0955912..483d0dd9c90 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -12,10 +12,6 @@ module API
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
helpers do
- def user_project
- @project ||= find_project!(params[:project_id])
- end
-
def project_or_group
user_group
end
@@ -55,7 +51,7 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'pool/:distribution/:project_id/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
- present_distribution_package_file!
+ present_distribution_package_file!(find_project!(params[:project_id]))
end
end
end
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
index 23a542e4183..353f64b8dd1 100644
--- a/lib/api/debian_project_packages.rb
+++ b/lib/api/debian_project_packages.rb
@@ -21,16 +21,16 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
helpers do
def project_or_group
- user_project
+ user_project(action: :read_package)
end
end
after_validation do
require_packages_enabled!
- not_found! unless ::Feature.enabled?(:debian_packages, user_project)
+ not_found! unless ::Feature.enabled?(:debian_packages, project_or_group)
- authorize_read_package!
+ authorize_read_package!(project_or_group)
end
params do
@@ -58,7 +58,7 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'pool/:distribution/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
- present_distribution_package_file!
+ present_distribution_package_file!(project_or_group)
end
params do
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index d3a25a076a0..768ffac41ce 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -254,7 +254,7 @@ module API
def readable_discussion_notes(noteable, discussion_ids)
notes = noteable.notes
.with_discussion_ids(discussion_ids)
- .inc_relations_for_view
+ .inc_relations_for_view(noteable)
.includes(:noteable)
.fresh
diff --git a/lib/api/entities/appearance.rb b/lib/api/entities/appearance.rb
index 94a39568393..cabdf68c23a 100644
--- a/lib/api/entities/appearance.rb
+++ b/lib/api/entities/appearance.rb
@@ -4,13 +4,17 @@ module API
module Entities
class Appearance < Grape::Entity
expose :title
- expose :short_title
+ expose :pwa_short_name
expose :description
expose :logo do |appearance, options|
appearance.logo.url
end
+ expose :pwa_icon do |appearance, options|
+ appearance.pwa_icon.url
+ end
+
expose :header_logo do |appearance, options|
appearance.header_logo.url
end
diff --git a/lib/api/entities/application_setting.rb b/lib/api/entities/application_setting.rb
index db51d4380d0..8aace9126d6 100644
--- a/lib/api/entities/application_setting.rb
+++ b/lib/api/entities/application_setting.rb
@@ -43,6 +43,11 @@ module API
# This field is deprecated and always returns true
expose(:housekeeping_bitmaps_enabled) { |_settings, _options| true }
+
+ # These fields are deprecated and always returns housekeeping_optimize_repository_period value
+ expose(:housekeeping_full_repack_period) { |settings, _options| settings.housekeeping_optimize_repository_period }
+ expose(:housekeeping_gc_period) { |settings, _options| settings.housekeeping_optimize_repository_period }
+ expose(:housekeeping_incremental_repack_period) { |settings, _options| settings.housekeeping_optimize_repository_period }
end
end
end
diff --git a/lib/api/entities/basic_project_details.rb b/lib/api/entities/basic_project_details.rb
index 2585b2d0b6d..f89e5adca6d 100644
--- a/lib/api/entities/basic_project_details.rb
+++ b/lib/api/entities/basic_project_details.rb
@@ -15,7 +15,10 @@ module API
expose :ssh_url_to_repo, documentation: { type: 'string', example: 'git@gitlab.example.com:gitlab/gitlab.git' }
expose :http_url_to_repo, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab.git' }
expose :web_url, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab' }
- expose :readme_url, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab/blob/master/README.md' }
+ with_options if: ->(_, _) { user_has_access_to_project_repository? } do
+ expose :readme_url, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab/blob/master/README.md' }
+ expose :forks_count, documentation: { type: 'integer', example: 1 }
+ end
expose :license_url, if: :license, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab/blob/master/LICENCE' } do |project|
license = project.repository.license_blob
@@ -33,7 +36,6 @@ module API
project.avatar_url(only_path: false)
end
- expose :forks_count, documentation: { type: 'integer', example: 1 }
expose :star_count, documentation: { type: 'integer', example: 1 }
expose :last_activity_at, documentation: { type: 'dateTime', example: '2013-09-30T13:46:02Z' }
expose :namespace, using: 'API::Entities::NamespaceBasic'
@@ -74,6 +76,10 @@ module API
project.topics.pluck(:name).sort # rubocop:disable CodeReuse/ActiveRecord
end
end
+
+ def user_has_access_to_project_repository?
+ Ability.allowed?(options[:current_user], :read_code, project)
+ end
end
end
end
diff --git a/lib/api/entities/bulk_imports/entity.rb b/lib/api/entities/bulk_imports/entity.rb
index 8f9fbe57935..176d10b2580 100644
--- a/lib/api/entities/bulk_imports/entity.rb
+++ b/lib/api/entities/bulk_imports/entity.rb
@@ -9,7 +9,11 @@ module API
expose :status_name, as: :status, documentation: {
type: 'string', example: 'created', values: %w[created started finished timeout failed]
}
+ expose :entity_type, documentation: { type: 'string', values: %w[group project] }
expose :source_full_path, documentation: { type: 'string', example: 'source_group' }
+ expose :full_path, as: :destination_full_path, documentation: {
+ type: 'string', example: 'some_group/source_project'
+ }
expose :destination_name, documentation: { type: 'string', example: 'destination_slug' } # deprecated
expose :destination_slug, documentation: { type: 'string', example: 'destination_slug' }
expose :destination_namespace, documentation: { type: 'string', example: 'destination_path' }
@@ -19,6 +23,7 @@ module API
expose :created_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' }
expose :updated_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' }
expose :failures, using: EntityFailure, documentation: { is_array: true }
+ expose :migrate_projects, documentation: { type: 'boolean', example: true }
end
end
end
diff --git a/lib/api/entities/ml/mlflow/run_info.rb b/lib/api/entities/ml/mlflow/run_info.rb
index d3934545ba4..60e4416e011 100644
--- a/lib/api/entities/ml/mlflow/run_info.rb
+++ b/lib/api/entities/ml/mlflow/run_info.rb
@@ -10,6 +10,7 @@ module API
expose(:experiment_id) { |candidate| candidate.experiment.iid.to_s }
expose(:start_time) { |candidate| candidate.start_time || 0 }
expose :end_time, expose_nil: false
+ expose :name, as: :run_name, expose_nil: false
expose(:status) { |candidate| candidate.status.to_s.upcase }
expose(:artifact_uri) { |candidate, options| "#{options[:packages_url]}#{candidate.artifact_root}" }
expose(:lifecycle_stage) { |candidate| 'active' }
diff --git a/lib/api/entities/project_integration_basic.rb b/lib/api/entities/project_integration_basic.rb
index aa0ad158b83..b7c56d7cca1 100644
--- a/lib/api/entities/project_integration_basic.rb
+++ b/lib/api/entities/project_integration_basic.rb
@@ -14,6 +14,7 @@ module API
expose :commit_events, documentation: { type: 'boolean' }
expose :push_events, documentation: { type: 'boolean' }
expose :issues_events, documentation: { type: 'boolean' }
+ expose :incident_events, documentation: { type: 'boolean' }
expose :confidential_issues_events, documentation: { type: 'boolean' }
expose :merge_requests_events, documentation: { type: 'boolean' }
expose :tag_push_events, documentation: { type: 'boolean' }
diff --git a/lib/api/entities/remote_mirror.rb b/lib/api/entities/remote_mirror.rb
index 9fb5b2697bc..f64ec71bffb 100644
--- a/lib/api/entities/remote_mirror.rb
+++ b/lib/api/entities/remote_mirror.rb
@@ -16,3 +16,5 @@ module API
end
end
end
+
+API::Entities::RemoteMirror.prepend_mod
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 01d46ee7bfb..64510a9615a 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -12,6 +12,8 @@ module API
feature_category :continuous_delivery
urgency :low
+ MIN_SEARCH_LENGTH = 3
+
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
end
@@ -29,7 +31,7 @@ module API
params do
use :pagination
optional :name, type: String, desc: 'Return the environment with this name. Mutually exclusive with search'
- optional :search, type: String, desc: 'Return list of environments matching the search criteria. Mutually exclusive with name'
+ optional :search, type: String, desc: "Return list of environments matching the search criteria. Mutually exclusive with name. Must be at least #{MIN_SEARCH_LENGTH} characters."
optional :states,
type: String,
values: Environment.valid_states.map(&:to_s),
@@ -39,6 +41,10 @@ module API
get ':id/environments' do
authorize! :read_environment, user_project
+ if Feature.enabled?(:environment_search_api_min_chars, user_project) && params[:search].present? && params[:search].length < MIN_SEARCH_LENGTH
+ bad_request!("Search query is less than #{MIN_SEARCH_LENGTH} characters")
+ end
+
environments = ::Environments::EnvironmentsFinder.new(user_project, current_user, declared_params(include_missing: false)).execute
present paginate(environments), with: Entities::Environment, current_user: current_user
@@ -182,6 +188,35 @@ module API
present environment, with: Entities::Environment, current_user: current_user
end
+ desc 'Stop stale environments' do
+ detail 'It returns `200` if stale environment check was scheduled successfully'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' }
+ ]
+ tags %w[environments]
+ end
+ params do
+ requires :before,
+ type: DateTime,
+ desc: 'Stop all environments that were last modified or deployed to before this date.'
+ end
+ post ':id/environments/stop_stale' do
+ authorize! :stop_environment, user_project
+
+ bad_request!('Invalid Date') if params[:before] < 10.years.ago || params[:before] > 1.week.ago
+
+ service_response = ::Environments::StopStaleService.new(user_project, current_user, params.slice(:before)).execute
+
+ if service_response.error?
+ status 400
+ else
+ status 200
+ end
+
+ present message: service_response.message
+ end
+
desc 'Get a specific environment' do
success Entities::Environment
failure [
diff --git a/lib/api/files.rb b/lib/api/files.rb
index b02f1a8728b..18638abd184 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -151,19 +151,6 @@ module API
present blame_ranges, with: Entities::BlameRange
end
- desc 'Get raw file metadata from repository'
- params do
- requires :file_path, type: String, file_path: true,
- desc: 'The url encoded path to the file.', documentation: { example: 'lib%2Fclass%2Erb' }
- optional :ref, type: String,
- desc: 'The name of branch, tag or commit', allow_blank: false, documentation: { example: 'main' }
- end
- head ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
- assign_file_vars!
-
- set_http_headers(blob_data)
- end
-
desc 'Get raw file contents from the repository' do
success File
end
diff --git a/lib/api/group_debian_distributions.rb b/lib/api/group_debian_distributions.rb
index 0364e2e7b56..8b6d4b8c4b2 100644
--- a/lib/api/group_debian_distributions.rb
+++ b/lib/api/group_debian_distributions.rb
@@ -19,7 +19,7 @@ module API
namespace ':id/-' do
helpers do
- def project_or_group
+ def project_or_group(_ = nil)
user_group
end
end
diff --git a/lib/api/group_export.rb b/lib/api/group_export.rb
index eb0a01e0d3d..37dfbfdb925 100644
--- a/lib/api/group_export.rb
+++ b/lib/api/group_export.rb
@@ -64,67 +64,73 @@ module API
end
end
- desc 'Start relations export' do
- detail 'This feature was introduced in GitLab 13.12'
- tags %w[group_export]
- success code: 202
- failure [
- { code: 401, message: 'Unauthorized' },
- { code: 403, message: 'Forbidden' },
- { code: 404, message: 'Not found' },
- { code: 503, message: 'Service unavailable' }
- ]
- end
- post ':id/export_relations' do
- response = ::BulkImports::ExportService.new(portable: user_group, user: current_user).execute
+ resource do
+ before do
+ not_found! unless Gitlab::CurrentSettings.bulk_import_enabled?
+ end
- if response.success?
- accepted!
- else
- render_api_error!(message: 'Group relations export could not be started.')
+ desc 'Start relations export' do
+ detail 'This feature was introduced in GitLab 13.12'
+ tags %w[group_export]
+ success code: 202
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 503, message: 'Service unavailable' }
+ ]
end
- end
+ post ':id/export_relations' do
+ response = ::BulkImports::ExportService.new(portable: user_group, user: current_user).execute
- desc 'Download relations export' do
- detail 'This feature was introduced in GitLab 13.12'
- produces %w[application/octet-stream application/json]
- tags %w[group_export]
- success code: 200
- failure [
- { code: 401, message: 'Unauthorized' },
- { code: 403, message: 'Forbidden' },
- { code: 404, message: 'Not found' },
- { code: 503, message: 'Service unavailable' }
- ]
- end
- params do
- requires :relation, type: String, desc: 'Group relation name'
- end
- get ':id/export_relations/download' do
- export = user_group.bulk_import_exports.find_by_relation(params[:relation])
- file = export&.upload&.export_file
+ if response.success?
+ accepted!
+ else
+ render_api_error!(message: 'Group relations export could not be started.')
+ end
+ end
- if file
- present_carrierwave_file!(file)
- else
- render_api_error!('404 Not found', 404)
+ desc 'Download relations export' do
+ detail 'This feature was introduced in GitLab 13.12'
+ produces %w[application/octet-stream application/json]
+ tags %w[group_export]
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 503, message: 'Service unavailable' }
+ ]
end
- end
+ params do
+ requires :relation, type: String, desc: 'Group relation name'
+ end
+ get ':id/export_relations/download' do
+ export = user_group.bulk_import_exports.find_by_relation(params[:relation])
+ file = export&.upload&.export_file
- desc 'Relations export status' do
- detail 'This feature was introduced in GitLab 13.12'
- is_array true
- tags %w[group_export]
- success code: 200, model: Entities::BulkImports::ExportStatus
- failure [
- { code: 401, message: 'Unauthorized' },
- { code: 403, message: 'Forbidden' },
- { code: 404, message: 'Not found' },
- { code: 503, message: 'Service unavailable' }
- ]
- end
- get ':id/export_relations/status' do
- present user_group.bulk_import_exports, with: Entities::BulkImports::ExportStatus
+ if file
+ present_carrierwave_file!(file)
+ else
+ render_api_error!('404 Not found', 404)
+ end
+ end
+
+ desc 'Relations export status' do
+ detail 'This feature was introduced in GitLab 13.12'
+ is_array true
+ tags %w[group_export]
+ success code: 200, model: Entities::BulkImports::ExportStatus
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 503, message: 'Service unavailable' }
+ ]
+ end
+ get ':id/export_relations/status' do
+ present user_group.bulk_import_exports, with: Entities::BulkImports::ExportStatus
+ end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 0b5a471ea12..38430aac455 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -608,6 +608,8 @@ module API
if file.file_storage?
present_disk_file!(file.path, file.filename)
elsif supports_direct_download && file.class.direct_download_enabled?
+ return redirect(signed_head_url(file)) if head_request_on_aws_file?(file)
+
redirect(cdn_fronted_url(file))
else
header(*Gitlab::Workhorse.send_url(file.url))
@@ -695,8 +697,31 @@ module API
unprocessable_entity!('User must be authenticated to use search')
end
+ def validate_search_rate_limit!
+ return unless Feature.enabled?(:rate_limit_issuable_searches)
+
+ if current_user
+ check_rate_limit!(:search_rate_limit, scope: [current_user])
+ else
+ check_rate_limit!(:search_rate_limit_unauthenticated, scope: [ip_address])
+ end
+ end
+
private
+ def head_request_on_aws_file?(file)
+ request.head? && file.fog_credentials[:provider] == 'AWS'
+ end
+
+ def signed_head_url(file)
+ fog_storage = ::Fog::Storage.new(file.fog_credentials)
+ fog_dir = fog_storage.directories.new(key: file.fog_directory)
+ fog_file = fog_dir.files.new(key: file.path)
+ expire_at = ::Fog::Time.now + file.fog_authenticated_url_expiration
+
+ fog_file.collection.head_url(fog_file.key, expire_at)
+ end
+
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def initial_current_user
return @initial_current_user if defined?(@initial_current_user)
diff --git a/lib/api/helpers/award_emoji.rb b/lib/api/helpers/award_emoji.rb
index f8417366ea4..f0b3cafc3d2 100644
--- a/lib/api/helpers/award_emoji.rb
+++ b/lib/api/helpers/award_emoji.rb
@@ -6,7 +6,7 @@ module API
def self.awardables
[
{ type: 'issue', resource: :projects, find_by: :iid, feature_category: :team_planning },
- { type: 'merge_request', resource: :projects, find_by: :iid, feature_category: :code_review },
+ { type: 'merge_request', resource: :projects, find_by: :iid, feature_category: :code_review_workflow },
{ type: 'snippet', resource: :projects, find_by: :id, feature_category: :source_code_management }
]
end
diff --git a/lib/api/helpers/discussions_helpers.rb b/lib/api/helpers/discussions_helpers.rb
index 182ada54a12..d497bc66015 100644
--- a/lib/api/helpers/discussions_helpers.rb
+++ b/lib/api/helpers/discussions_helpers.rb
@@ -9,8 +9,8 @@ module API
{
Issue => :team_planning,
Snippet => :source_code_management,
- MergeRequest => :code_review,
- Commit => :code_review
+ MergeRequest => :code_review_workflow,
+ Commit => :code_review_workflow
}
end
end
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index 543449c0349..31328facd69 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -63,6 +63,12 @@ module API
},
{
required: false,
+ name: :incident_channel,
+ type: String,
+ desc: 'The name of the channel to receive incident_events notifications'
+ },
+ {
+ required: false,
name: :confidential_issue_channel,
type: String,
desc: 'The name of the channel to receive confidential_issues_events notifications'
@@ -116,6 +122,12 @@ module API
},
{
required: false,
+ name: :incident_events,
+ type: Boolean,
+ desc: 'Enable notifications for incident_events'
+ },
+ {
+ required: false,
name: :confidential_issues_events,
type: Boolean,
desc: 'Enable notifications for confidential_issues_events'
@@ -161,6 +173,26 @@ module API
def self.integrations
{
+ 'apple-app-store' => [
+ {
+ required: true,
+ name: :app_store_issuer_id,
+ type: String,
+ desc: 'The Apple App Store Connect Issuer ID'
+ },
+ {
+ required: true,
+ name: :app_store_key_id,
+ type: String,
+ desc: 'The Apple App Store Connect Key ID'
+ },
+ {
+ required: true,
+ name: :app_store_private_key,
+ type: String,
+ desc: 'The Apple App Store Connect Private Key'
+ }
+ ],
'asana' => [
{
required: true,
@@ -871,6 +903,7 @@ module API
def self.integration_classes
[
+ ::Integrations::AppleAppStore,
::Integrations::Asana,
::Integrations::Assembla,
::Integrations::Bamboo,
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index 6a3cf5c87ae..b0ea4388d9b 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -40,6 +40,9 @@ module API
end
def source_members(source)
+ return source.namespace_members if source.is_a?(Project) &&
+ Feature.enabled?(:project_members_index_by_project_namespace, source)
+
source.members
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index 302dac4abf7..da499abe475 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -8,7 +8,7 @@ module API
def self.feature_category_per_noteable_type
{
Issue => :team_planning,
- MergeRequest => :code_review,
+ MergeRequest => :code_review_workflow,
Snippet => :source_code_management
}
end
diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb
index 8d913268405..1d35c316913 100644
--- a/lib/api/helpers/packages_helpers.rb
+++ b/lib/api/helpers/packages_helpers.rb
@@ -6,6 +6,7 @@ module API
extend ::Gitlab::Utils::Override
MAX_PACKAGE_FILE_SIZE = 50.megabytes.freeze
+ ALLOWED_REQUIRED_PERMISSIONS = %i[read_package read_group].freeze
def require_packages_enabled!
not_found! unless ::Gitlab.config.packages.enabled
@@ -27,9 +28,15 @@ module API
authorize!(:destroy_package, subject)
end
- def authorize_packages_access!(subject = user_project)
+ def authorize_packages_access!(subject = user_project, required_permission = :read_package)
require_packages_enabled!
- authorize_read_package!(subject)
+ return forbidden! unless required_permission.in?(ALLOWED_REQUIRED_PERMISSIONS)
+
+ if required_permission == :read_package
+ authorize_read_package!(subject)
+ else
+ authorize!(required_permission, subject)
+ end
end
def authorize_workhorse!(subject: user_project, has_length: true, maximum_size: MAX_PACKAGE_FILE_SIZE)
diff --git a/lib/api/helpers/pagination_strategies.rb b/lib/api/helpers/pagination_strategies.rb
index 4e244ea589e..5fbc3081ee8 100644
--- a/lib/api/helpers/pagination_strategies.rb
+++ b/lib/api/helpers/pagination_strategies.rb
@@ -3,13 +3,14 @@
module API
module Helpers
module PaginationStrategies
- def paginate_with_strategies(relation, request_scope = nil)
+ # paginator_params are only currently supported with offset pagination
+ def paginate_with_strategies(relation, request_scope = nil, paginator_params: {})
paginator = paginator(relation, request_scope)
result = if block_given?
- yield(paginator.paginate(relation))
+ yield(paginator.paginate(relation, **paginator_params))
else
- paginator.paginate(relation)
+ paginator.paginate(relation, **paginator_params)
end
result.tap do |records, _|
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 9d370176e62..c5636fa06de 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -205,6 +205,9 @@ module API
def filter_attributes_using_license!(attrs)
end
+ def filter_attributes_under_feature_flag!(attrs, project)
+ end
+
def validate_git_import_url!(import_url)
return if import_url.blank?
diff --git a/lib/api/helpers/rate_limiter.rb b/lib/api/helpers/rate_limiter.rb
index 03f3cd649b1..be92277c25a 100644
--- a/lib/api/helpers/rate_limiter.rb
+++ b/lib/api/helpers/rate_limiter.rb
@@ -10,25 +10,14 @@ module API
# See app/controllers/concerns/check_rate_limit.rb for Rails controllers version
module RateLimiter
def check_rate_limit!(key, scope:, **options)
- return if bypass_header_set?
- return unless rate_limiter.throttled?(key, scope: scope, **options)
-
- rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
+ return unless Gitlab::ApplicationRateLimiter.throttled_request?(
+ request, current_user, key, scope: scope, **options
+ )
return yield if block_given?
render_api_error!({ error: _('This endpoint has been requested too many times. Try again later.') }, 429)
end
-
- private
-
- def rate_limiter
- ::Gitlab::ApplicationRateLimiter
- end
-
- def bypass_header_set?
- ::Gitlab::Throttle.bypass_header.present? && request.get_header(Gitlab::Throttle.bypass_header) == '1'
- end
end
end
end
diff --git a/lib/api/helpers/remote_mirrors_helpers.rb b/lib/api/helpers/remote_mirrors_helpers.rb
new file mode 100644
index 00000000000..efd81a5ac5a
--- /dev/null
+++ b/lib/api/helpers/remote_mirrors_helpers.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module RemoteMirrorsHelpers
+ extend ActiveSupport::Concern
+ extend Grape::API::Helpers
+
+ params :mirror_branches_setting_ce do
+ optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored'
+ end
+
+ params :mirror_branches_setting_ee do
+ end
+
+ params :mirror_branches_setting do
+ use :mirror_branches_setting_ce
+ use :mirror_branches_setting_ee
+ end
+
+ def verify_mirror_branches_setting(attrs, project); end
+ end
+ end
+end
+
+API::Helpers::RemoteMirrorsHelpers.prepend_mod
diff --git a/lib/api/helpers/resource_events_helpers.rb b/lib/api/helpers/resource_events_helpers.rb
index c47a58e8fce..11cb65056cd 100644
--- a/lib/api/helpers/resource_events_helpers.rb
+++ b/lib/api/helpers/resource_events_helpers.rb
@@ -7,7 +7,7 @@ module API
# This is a method instead of a constant, allowing EE to more easily extend it.
{
Issue => { feature_category: :team_planning, id_field: 'IID' },
- MergeRequest => { feature_category: :code_review, id_field: 'IID' }
+ MergeRequest => { feature_category: :code_review_workflow, id_field: 'IID' }
}
end
end
diff --git a/lib/api/import_github.rb b/lib/api/import_github.rb
index d742e3732a8..6330a4458f3 100644
--- a/lib/api/import_github.rb
+++ b/lib/api/import_github.rb
@@ -8,6 +8,7 @@ module API
urgency :low
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
+ rescue_from Gitlab::GithubImport::RateLimitError, with: :too_many_requests
helpers do
def client
@@ -33,6 +34,10 @@ module API
def provider_unauthorized
error!("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.", 401)
end
+
+ def too_many_requests
+ error!('Too Many Requests', 429)
+ end
end
desc 'Import a GitHub project' do
@@ -51,7 +56,7 @@ module API
requires :personal_access_token, type: String, desc: 'GitHub personal access token'
requires :repo_id, type: Integer, desc: 'GitHub repository ID'
optional :new_name, type: String, desc: 'New repo name'
- requires :target_namespace, type: String, desc: 'Namespace to import repo into'
+ requires :target_namespace, type: String, allow_blank: false, desc: 'Namespace or group to import repository into'
optional :github_hostname, type: String, desc: 'Custom GitHub enterprise hostname'
optional :optional_stages, type: Hash, desc: 'Optional stages of import to be performed'
end
@@ -92,5 +97,32 @@ module API
render_api_error!(result[:message], result[:http_status])
end
end
+
+ desc 'Import User Gists' do
+ detail 'This feature was introduced in GitLab 15.8'
+ success code: 202
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 422, message: 'Unprocessable Entity' },
+ { code: 429, message: 'Too Many Requests' }
+ ]
+ end
+ params do
+ requires :personal_access_token, type: String, desc: 'GitHub personal access token'
+ end
+ post 'import/github/gists' do
+ not_found! if Feature.disabled?(:github_import_gists)
+
+ authorize! :create_snippet
+
+ result = Import::Github::GistsImportService.new(current_user, client, access_params).execute
+
+ if result[:status] == :success
+ status 202
+ else
+ status result[:http_status]
+ { errors: result[:message] }
+ end
+ end
end
end
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index dbd5c5f9db1..3f6e052f7b6 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -44,16 +44,12 @@ module API
# This is a separate method so that EE can alter its behaviour more
# easily.
- if Feature.enabled?(:rate_limit_gitlab_shell)
- check_rate_limit!(:gitlab_shell_operation, scope: [params[:action], params[:project], actor.key_or_user])
- end
+ check_rate_limit!(:gitlab_shell_operation, scope: [params[:action], params[:project], actor.key_or_user])
- if Feature.enabled?(:rate_limit_gitlab_shell_by_ip, actor.user)
- rate_limiter = Gitlab::Auth::IpRateLimiter.new(request.ip)
+ rate_limiter = Gitlab::Auth::IpRateLimiter.new(request.ip)
- unless rate_limiter.trusted_ip?
- check_rate_limit!(:gitlab_shell_operation, scope: [params[:action], params[:project], rate_limiter.ip])
- end
+ unless rate_limiter.trusted_ip?
+ check_rate_limit!(:gitlab_shell_operation, scope: [params[:action], params[:project], rate_limiter.ip])
end
# Stores some Git-specific env thread-safely
diff --git a/lib/api/invitations.rb b/lib/api/invitations.rb
index 6aefdf146cf..872dab26469 100644
--- a/lib/api/invitations.rb
+++ b/lib/api/invitations.rb
@@ -90,7 +90,7 @@ module API
.new(current_user, update_params)
.execute(invite)
- updated_member = result[:member]
+ updated_member = result[:members].first
if result[:status] == :success
present_members updated_member
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index b08819e34e3..7b6306938cf 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -116,6 +116,7 @@ module API
get '/issues_statistics' do
authenticate! unless params[:scope] == 'all'
validate_anonymous_search_access! if params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
present issues_statistics, with: Grape::Presenters::Presenter
end
@@ -134,6 +135,7 @@ module API
get do
authenticate! unless params[:scope] == 'all'
validate_anonymous_search_access! if params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
issues = paginate(find_issues)
options = {
@@ -173,6 +175,7 @@ module API
end
get ":id/issues" do
validate_anonymous_search_access! if declared_params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
issues = paginate(find_issues(group_id: user_group.id, include_subgroups: true))
options = {
@@ -192,6 +195,7 @@ module API
end
get ":id/issues_statistics" do
validate_anonymous_search_access! if declared_params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
present issues_statistics(group_id: user_group.id, include_subgroups: true), with: Grape::Presenters::Presenter
end
@@ -211,6 +215,7 @@ module API
end
get ":id/issues" do
validate_anonymous_search_access! if declared_params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
issues = paginate(find_issues(project_id: user_project.id))
options = {
@@ -230,6 +235,7 @@ module API
end
get ":id/issues_statistics" do
validate_anonymous_search_access! if declared_params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
present issues_statistics(project_id: user_project.id), with: Grape::Presenters::Presenter
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 76f4364106b..32c5227a939 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -151,7 +151,7 @@ module API
.new(current_user, declared_params(include_missing: false))
.execute(member)
- updated_member = result[:member]
+ updated_member = result[:members].first
if result[:status] == :success
present_members updated_member
diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb
index c7f0f88eacc..e7193035ce0 100644
--- a/lib/api/merge_request_diffs.rb
+++ b/lib/api/merge_request_diffs.rb
@@ -7,7 +7,7 @@ module API
before { authenticate! }
- feature_category :code_review
+ feature_category :code_review_workflow
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index a9572cf7ce6..25fbeca01dc 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -13,7 +13,7 @@ module API
# These endpoints are defined in `TimeTrackingEndpoints` and is shared by
# API::Issues. In order to be able to define the feature category of these
# endpoints, we need to define them at the top-level by route.
- feature_category :code_review, [
+ feature_category :code_review_workflow, [
'/projects/:id/merge_requests/:merge_request_iid/time_estimate',
'/projects/:id/merge_requests/:merge_request_iid/reset_time_estimate',
'/projects/:id/merge_requests/:merge_request_iid/add_spent_time',
@@ -105,20 +105,12 @@ module API
options
end
- def authorize_push_to_merge_request!(merge_request)
- forbidden!('Source branch does not exist') unless
- merge_request.source_branch_exists?
+ def authorize_merge_request_rebase!(merge_request)
+ result = ::MergeRequests::RebaseService
+ .new(project: merge_request.source_project, current_user: current_user)
+ .validate(merge_request)
- user_access = Gitlab::UserAccess.new(
- current_user,
- container: merge_request.source_project
- )
-
- forbidden!('Cannot push to source branch') unless
- user_access.can_push_to_branch?(merge_request.source_branch)
-
- forbidden!('Source branch is protected from force push') unless
- merge_request.permits_force_push?
+ forbidden!(result.message) if result.error?
end
def recheck_mergeability_of(merge_requests:)
@@ -146,9 +138,10 @@ module API
use :merge_requests_params
use :optional_scope_param
end
- get feature_category: :code_review, urgency: :low do
+ get feature_category: :code_review_workflow, urgency: :low do
authenticate! unless params[:scope] == 'all'
validate_anonymous_search_access! if params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
merge_requests = find_merge_requests
present merge_requests, serializer_options_for(merge_requests)
@@ -175,8 +168,9 @@ module API
default: true,
desc: 'Returns merge requests from non archived projects only.'
end
- get ":id/merge_requests", feature_category: :code_review, urgency: :low do
+ get ":id/merge_requests", feature_category: :code_review_workflow, urgency: :low do
validate_anonymous_search_access! if declared_params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
merge_requests = find_merge_requests(group_id: user_group.id, include_subgroups: true)
present merge_requests, serializer_options_for(merge_requests).merge(group: user_group)
@@ -241,9 +235,10 @@ module API
desc: 'Returns the request having the given `iid`.',
documentation: { is_array: true }
end
- get ":id/merge_requests", feature_category: :code_review, urgency: :low do
+ get ":id/merge_requests", feature_category: :code_review_workflow, urgency: :low do
authorize! :read_merge_request, user_project
validate_anonymous_search_access! if declared_params[:search].present?
+ validate_search_rate_limit! if declared_params[:search].present?
merge_requests = find_merge_requests(project_id: user_project.id)
@@ -286,7 +281,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, urgency: :low do
+ post ":id/merge_requests", feature_category: :code_review_workflow, urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20770')
authorize! :create_merge_request_from, user_project
@@ -314,7 +309,7 @@ module API
params do
requires :merge_request_iid, type: Integer, desc: 'The internal ID of the merge request.'
end
- delete ":id/merge_requests/:merge_request_iid", feature_category: :code_review, urgency: :low do
+ delete ":id/merge_requests/:merge_request_iid", feature_category: :code_review_workflow, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
authorize!(:destroy_merge_request, merge_request)
@@ -339,7 +334,7 @@ module API
]
tags %w[merge_requests]
end
- get ':id/merge_requests/:merge_request_iid', feature_category: :code_review, urgency: :low do
+ get ':id/merge_requests/:merge_request_iid', feature_category: :code_review_workflow, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request,
@@ -360,7 +355,7 @@ module API
]
tags %w[merge_requests]
end
- get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review, urgency: :low do
+ get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review_workflow, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
participants = ::Kaminari.paginate_array(merge_request.visible_participants(current_user))
@@ -376,7 +371,7 @@ module API
]
tags %w[merge_requests]
end
- get ':id/merge_requests/:merge_request_iid/reviewers', feature_category: :code_review, urgency: :low do
+ get ':id/merge_requests/:merge_request_iid/reviewers', feature_category: :code_review_workflow, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
reviewers = ::Kaminari.paginate_array(merge_request.merge_request_reviewers)
@@ -392,7 +387,7 @@ module API
]
tags %w[merge_requests]
end
- get ':id/merge_requests/:merge_request_iid/commits', feature_category: :code_review, urgency: :low do
+ get ':id/merge_requests/:merge_request_iid/commits', feature_category: :code_review_workflow, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
commits =
@@ -410,7 +405,7 @@ module API
]
tags %w[merge_requests]
end
- get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review, urgency: :high do
+ get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review_workflow, urgency: :high do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
context_commits =
paginate(merge_request.merge_request_context_commits).map(&:to_commit)
@@ -434,7 +429,7 @@ module API
]
tags %w[merge_requests]
end
- post ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
+ post ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review_workflow do
commit_ids = params[:commits]
if commit_ids.size > CONTEXT_COMMITS_POST_LIMIT
@@ -471,7 +466,7 @@ module API
]
tags %w[merge_requests]
end
- delete ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
+ delete ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review_workflow do
commit_ids = params[:commits]
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -495,7 +490,7 @@ module API
]
tags %w[merge_requests]
end
- get ':id/merge_requests/:merge_request_iid/changes', feature_category: :code_review, urgency: :low do
+ get ':id/merge_requests/:merge_request_iid/changes', feature_category: :code_review_workflow, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request,
@@ -517,7 +512,7 @@ module API
params do
use :pagination
end
- get ':id/merge_requests/:merge_request_iid/diffs', feature_category: :code_review, urgency: :low do
+ get ':id/merge_requests/:merge_request_iid/diffs', feature_category: :code_review_workflow, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present paginate(merge_request.merge_request_diff.paginated_diffs(params[:page], params[:per_page])).diffs, with: Entities::Diff
@@ -585,7 +580,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, urgency: :low do
+ put ':id/merge_requests/:merge_request_iid', feature_category: :code_review_workflow, 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)
@@ -627,7 +622,7 @@ module API
optional :sha, type: String, desc: 'If present, then this SHA must match the HEAD of the source branch, otherwise the merge fails.'
optional :squash, type: Grape::API::Boolean, desc: 'If `true`, the commits are squashed into a single commit on merge.'
end
- put ':id/merge_requests/:merge_request_iid/merge', feature_category: :code_review, urgency: :low do
+ put ':id/merge_requests/:merge_request_iid/merge', feature_category: :code_review_workflow, urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/4796')
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -678,7 +673,7 @@ module API
]
tags %w[merge_requests]
end
- get ':id/merge_requests/:merge_request_iid/merge_ref', feature_category: :code_review do
+ get ':id/merge_requests/:merge_request_iid/merge_ref', feature_category: :code_review_workflow do
merge_request = find_project_merge_request(params[:merge_request_iid])
result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute(recheck: true)
@@ -701,7 +696,7 @@ module API
]
tags %w[merge_requests]
end
- post ':id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds', feature_category: :code_review do
+ post ':id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds', feature_category: :code_review_workflow do
merge_request = find_project_merge_request(params[:merge_request_iid])
unauthorized! unless merge_request.can_cancel_auto_merge?(current_user)
@@ -721,10 +716,10 @@ module API
params do
optional :skip_ci, type: Boolean, desc: 'Set to true to skip creating a CI pipeline.'
end
- put ':id/merge_requests/:merge_request_iid/rebase', feature_category: :code_review, urgency: :low do
+ put ':id/merge_requests/:merge_request_iid/rebase', feature_category: :code_review_workflow, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
- authorize_push_to_merge_request!(merge_request)
+ authorize_merge_request_rebase!(merge_request)
merge_request.rebase_async(current_user.id, skip_ci: params[:skip_ci])
@@ -744,7 +739,7 @@ module API
params do
use :pagination
end
- get ':id/merge_requests/:merge_request_iid/closes_issues', feature_category: :code_review, urgency: :low do
+ get ':id/merge_requests/:merge_request_iid/closes_issues', feature_category: :code_review_workflow, 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/ml/mlflow.rb b/lib/api/ml/mlflow.rb
index 54bbe0ee465..e7ed8e2e70c 100644
--- a/lib/api/ml/mlflow.rb
+++ b/lib/api/ml/mlflow.rb
@@ -166,9 +166,10 @@ module API
default: 0
optional :user_id, type: String, desc: 'This will be ignored'
optional :tags, type: Array, desc: 'Tags are stored, but not displayed'
+ optional :run_name, type: String, desc: 'A name for this run'
end
post 'create', urgency: :low do
- present candidate_repository.create!(experiment, params[:start_time], params[:tags]),
+ present candidate_repository.create!(experiment, params[:start_time], params[:tags], params[:run_name]),
with: Entities::Ml::Mlflow::Run, packages_url: packages_url
end
diff --git a/lib/api/nuget_group_packages.rb b/lib/api/nuget_group_packages.rb
index c93b24ee544..2afcb915b06 100644
--- a/lib/api/nuget_group_packages.rb
+++ b/lib/api/nuget_group_packages.rb
@@ -42,6 +42,10 @@ module API
def snowplow_gitlab_standard_context
{ namespace: find_authorized_group! }
end
+
+ def required_permission
+ :read_group
+ end
end
params do
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
index aa517661791..8e974cb9cbe 100644
--- a/lib/api/nuget_project_packages.rb
+++ b/lib/api/nuget_project_packages.rb
@@ -90,6 +90,10 @@ module API
created!
end
+
+ def required_permission
+ :read_package
+ end
end
params do
diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb
index 967847a8e62..15c1a78839f 100644
--- a/lib/api/pages_domains.rb
+++ b/lib/api/pages_domains.rb
@@ -54,7 +54,7 @@ module API
end
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before do
@@ -63,6 +63,8 @@ module API
desc 'Get all pages domains' do
success Entities::PagesDomain
+ tags %w[pages_domains]
+ is_array true
end
params do
use :pagination
diff --git a/lib/api/project_debian_distributions.rb b/lib/api/project_debian_distributions.rb
index 1e27f5c8856..856b4097b5a 100644
--- a/lib/api/project_debian_distributions.rb
+++ b/lib/api/project_debian_distributions.rb
@@ -14,13 +14,13 @@ module API
after_validation do
require_packages_enabled!
- not_found! unless ::Feature.enabled?(:debian_packages, user_project)
+ not_found! unless ::Feature.enabled?(:debian_packages, project_or_group)
end
namespace ':id' do
helpers do
- def project_or_group
- user_project
+ def project_or_group(action = :read_package)
+ user_project(action: action)
end
end
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index e4e950fb603..19e5ed3f9e0 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -5,109 +5,114 @@ module API
feature_category :importers
urgency :low
- before do
- not_found! unless Gitlab::CurrentSettings.project_export_enabled?
- authorize_admin_project
- end
-
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: { id: %r{[^/]+} } do
- desc 'Get export status' do
- detail 'This feature was introduced in GitLab 10.6.'
- success code: 200, model: Entities::ProjectExportStatus
- failure [
- { code: 401, message: 'Unauthorized' },
- { code: 403, message: 'Forbidden' },
- { code: 404, message: 'Not found' },
- { code: 503, message: 'Service unavailable' }
- ]
- tags ['project_export']
- end
- get ':id/export' do
- present user_project, with: Entities::ProjectExportStatus
- end
+ resource do
+ before do
+ not_found! unless Gitlab::CurrentSettings.project_export_enabled?
- desc 'Download export' do
- detail 'This feature was introduced in GitLab 10.6.'
- success code: 200
- failure [
- { code: 401, message: 'Unauthorized' },
- { code: 403, message: 'Forbidden' },
- { code: 404, message: 'Not found' },
- { code: 503, message: 'Service unavailable' }
- ]
- tags ['project_export']
- produces %w[application/octet-stream application/json]
- end
- get ':id/export/download' do
- check_rate_limit! :project_download_export, scope: [current_user, user_project.namespace]
+ authorize_admin_project
+ end
+
+ desc 'Get export status' do
+ detail 'This feature was introduced in GitLab 10.6.'
+ success code: 200, model: Entities::ProjectExportStatus
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 503, message: 'Service unavailable' }
+ ]
+ tags ['project_export']
+ end
+ get ':id/export' do
+ present user_project, with: Entities::ProjectExportStatus
+ end
- if user_project.export_file_exists?
- if user_project.export_archive_exists?
- present_carrierwave_file!(user_project.export_file)
+ desc 'Download export' do
+ detail 'This feature was introduced in GitLab 10.6.'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 503, message: 'Service unavailable' }
+ ]
+ tags ['project_export']
+ produces %w[application/octet-stream application/json]
+ end
+ get ':id/export/download' do
+ check_rate_limit! :project_download_export, scope: [current_user, user_project.namespace]
+
+ if user_project.export_file_exists?
+ if user_project.export_archive_exists?
+ present_carrierwave_file!(user_project.export_file)
+ else
+ render_api_error!('The project export file is not available yet', 404)
+ end
else
- render_api_error!('The project export file is not available yet', 404)
+ render_api_error!('404 Not found or has expired', 404)
end
- else
- render_api_error!('404 Not found or has expired', 404)
end
- end
- desc 'Start export' do
- detail 'This feature was introduced in GitLab 10.6.'
- success code: 202
- failure [
- { code: 400, message: 'Bad request' },
- { code: 401, message: 'Unauthorized' },
- { code: 403, message: 'Forbidden' },
- { code: 404, message: 'Not found' },
- { code: 429, message: 'Too many requests' },
- { code: 503, message: 'Service unavailable' }
- ]
- tags ['project_export']
- end
- params do
- optional :description, type: String, desc: 'Override the project description'
- optional :upload, type: Hash do
- optional :url, type: String, desc: 'The URL to upload the project'
- optional :http_method, type: String, default: 'PUT', values: %w[PUT POST],
- desc: 'HTTP method to upload the exported project'
+ desc 'Start export' do
+ detail 'This feature was introduced in GitLab 10.6.'
+ success code: 202
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 429, message: 'Too many requests' },
+ { code: 503, message: 'Service unavailable' }
+ ]
+ tags ['project_export']
end
- end
- post ':id/export' do
- check_rate_limit! :project_export, scope: current_user
+ params do
+ optional :description, type: String, desc: 'Override the project description'
+ optional :upload, type: Hash do
+ optional :url, type: String, desc: 'The URL to upload the project'
+ optional :http_method, type: String, default: 'PUT', values: %w[PUT POST],
+ desc: 'HTTP method to upload the exported project'
+ end
+ end
+ post ':id/export' do
+ check_rate_limit! :project_export, scope: current_user
- user_project.remove_exports
+ user_project.remove_exports
- project_export_params = declared_params(include_missing: false)
- after_export_params = project_export_params.delete(:upload) || {}
+ project_export_params = declared_params(include_missing: false)
+ after_export_params = project_export_params.delete(:upload) || {}
- export_strategy = if after_export_params[:url].present?
- params = after_export_params.slice(:url, :http_method).symbolize_keys
+ export_strategy = if after_export_params[:url].present?
+ params = after_export_params.slice(:url, :http_method).symbolize_keys
- Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy.new(**params)
- end
+ Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy.new(**params)
+ end
- if export_strategy&.invalid?
- render_validation_error!(export_strategy)
- else
- begin
- user_project.add_export_job(current_user: current_user,
- after_export_strategy: export_strategy,
- params: project_export_params)
- rescue Project::ExportLimitExceeded => e
- render_api_error!(e.message, 400)
+ if export_strategy&.invalid?
+ render_validation_error!(export_strategy)
+ else
+ begin
+ user_project.add_export_job(current_user: current_user,
+ after_export_strategy: export_strategy,
+ params: project_export_params)
+ rescue Project::ExportLimitExceeded => e
+ render_api_error!(e.message, 400)
+ end
end
- end
- accepted!
+ accepted!
+ end
end
resource do
before do
- not_found! unless ::Feature.enabled?(:bulk_import)
+ not_found! unless Gitlab::CurrentSettings.bulk_import_enabled?
+
+ authorize_admin_project
end
desc 'Start relations export' do
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index de39419b70b..5077f02fcc1 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -490,6 +490,7 @@ module API
attrs = translate_params_for_compatibility(attrs)
attrs = add_import_params(attrs)
filter_attributes_using_license!(attrs)
+ filter_attributes_under_feature_flag!(attrs, user_project)
verify_update_project_attrs!(user_project, attrs)
user_project.remove_avatar! if attrs.key?(:avatar) && attrs[:avatar].nil?
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index 0e83d086a6e..b21bcb4a903 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -56,7 +56,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the link. Link names must be unique in the release'
requires :url, type: String, desc: 'The URL of the link. Link URLs must be unique in the release.'
- optional :filepath, type: String, desc: 'Optional path for a direct asset link'
+ optional :direct_asset_path, type: String, desc: 'Optional path for a direct asset link', as: :filepath
optional :link_type,
type: String,
values: %w[other runbook image package],
@@ -108,7 +108,7 @@ module API
params do
optional :name, type: String, desc: 'The name of the link'
optional :url, type: String, desc: 'The URL of the link'
- optional :filepath, type: String, desc: 'Optional path for a direct asset link'
+ optional :direct_asset_path, type: String, desc: 'Optional path for a direct asset link', as: :filepath
optional :link_type,
type: String,
values: %w[other runbook image package],
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index e6884e66200..e69dc756551 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -150,18 +150,19 @@ module API
params do
requires :tag_name, type: String, desc: 'The Git tag the release is associated with', as: :tag
- requires :file_path,
+ requires :direct_asset_path,
type: String,
file_path: true,
- desc: 'The path to the file to download, as specified when creating the release asset'
+ desc: 'The path to the file to download, as specified when creating the release asset',
+ as: :filepath
end
route_setting :authentication, job_token_allowed: true
- get ':id/releases/:tag_name/downloads/*file_path', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
+ get ':id/releases/:tag_name/downloads/*direct_asset_path', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_read_code!
not_found! unless release
- link = release.links.find_by_filepath!("/#{params[:file_path]}")
+ link = release.links.find_by_filepath!("/#{params[:filepath]}")
not_found! unless link
@@ -237,7 +238,7 @@ module API
optional :links, type: Array do
requires :name, type: String, desc: 'The name of the link. Link names must be unique within the release'
requires :url, type: String, desc: 'The URL of the link. Link URLs must be unique within the release'
- optional :filepath, type: String, desc: 'Optional path for a direct asset link'
+ optional :direct_asset_path, type: String, desc: 'Optional path for a direct asset link', as: :filepath
optional :link_type, type: String, desc: 'The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`'
end
end
diff --git a/lib/api/remote_mirrors.rb b/lib/api/remote_mirrors.rb
index f7ea5a6ad2b..c3c7d9370e0 100644
--- a/lib/api/remote_mirrors.rb
+++ b/lib/api/remote_mirrors.rb
@@ -3,6 +3,7 @@
module API
class RemoteMirrors < ::API::Base
include PaginationParams
+ helpers Helpers::RemoteMirrorsHelpers
feature_category :source_code_management
@@ -60,14 +61,13 @@ module API
params do
requires :url, type: String, desc: 'The URL for a remote mirror', documentation: { example: 'https://*****:*****@example.com/gitlab/example.git' }
optional :enabled, type: Boolean, desc: 'Determines if the mirror is enabled', documentation: { example: false }
- optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored',
- documentation: { example: false }
optional :keep_divergent_refs, type: Boolean, desc: 'Determines if divergent refs are kept on the target',
documentation: { example: false }
+ use :mirror_branches_setting
end
post ':id/remote_mirrors' do
create_params = declared_params(include_missing: false)
-
+ verify_mirror_branches_setting(create_params, user_project)
new_mirror = user_project.remote_mirrors.create(create_params)
if new_mirror.persisted?
@@ -89,10 +89,9 @@ module API
params do
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
optional :enabled, type: Boolean, desc: 'Determines if the mirror is enabled', documentation: { example: true }
- optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored',
- documentation: { example: false }
optional :keep_divergent_refs, type: Boolean, desc: 'Determines if divergent refs are kept on the target',
documentation: { example: false }
+ use :mirror_branches_setting
end
put ':id/remote_mirrors/:mirror_id' do
mirror = user_project.remote_mirrors.find(params[:mirror_id])
@@ -100,6 +99,7 @@ module API
mirror_params = declared_params(include_missing: false)
mirror_params[:id] = mirror_params.delete(:mirror_id)
+ verify_mirror_branches_setting(mirror_params, user_project)
update_params = { remote_mirrors_attributes: mirror_params }
result = ::Projects::UpdateService
diff --git a/lib/api/resource_milestone_events.rb b/lib/api/resource_milestone_events.rb
index 5640e88ae6e..3eff3e8ad36 100644
--- a/lib/api/resource_milestone_events.rb
+++ b/lib/api/resource_milestone_events.rb
@@ -11,7 +11,7 @@ module API
{
Issue => :team_planning,
- MergeRequest => :code_review
+ MergeRequest => :code_review_workflow
}.each do |eventable_type, feature_category|
parent_type = eventable_type.parent_class.to_s.underscore
eventables_str = eventable_type.to_s.underscore.pluralize
diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb
index af0ceb1acfc..896d8fcc727 100644
--- a/lib/api/rubygem_packages.rb
+++ b/lib/api/rubygem_packages.rb
@@ -25,13 +25,19 @@ module API
.sent_through(:http_token)
end
+ helpers do
+ def project
+ user_project(action: :read_package)
+ end
+ end
+
before do
require_packages_enabled!
authenticate_non_get!
end
after_validation do
- not_found! unless Feature.enabled?(:rubygem_packages, user_project)
+ not_found! unless Feature.enabled?(:rubygem_packages, project)
end
params do
@@ -85,14 +91,14 @@ module API
requires :file_name, type: String, desc: 'Package file name', documentation: { type: 'file' }
end
get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do
- authorize_read_package!(user_project)
+ authorize_read_package!(project)
package_files = ::Packages::PackageFile
- .for_rubygem_with_file_name(user_project, params[:file_name])
+ .for_rubygem_with_file_name(project, params[:file_name])
package_file = package_files.installable.last!
- track_package_event('pull_package', :rubygems, project: user_project, namespace: user_project.namespace)
+ track_package_event('pull_package', :rubygems, project: project, namespace: project.namespace)
present_package_file!(package_file)
end
@@ -109,9 +115,9 @@ module API
end
post 'gems/authorize' do
authorize_workhorse!(
- subject: user_project,
+ subject: project,
has_length: false,
- maximum_size: user_project.actual_limits.rubygems_max_file_size
+ maximum_size: project.actual_limits.rubygems_max_file_size
)
end
@@ -129,16 +135,16 @@ module API
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
end
post 'gems' do
- authorize_upload!(user_project)
- bad_request!('File is too large') if user_project.actual_limits.exceeded?(:rubygems_max_file_size, params[:file].size)
+ authorize_upload!(project)
+ bad_request!('File is too large') if project.actual_limits.exceeded?(:rubygems_max_file_size, params[:file].size)
- track_package_event('push_package', :rubygems, user: current_user, project: user_project, namespace: user_project.namespace)
+ track_package_event('push_package', :rubygems, user: current_user, project: project, namespace: project.namespace)
package_file = nil
ApplicationRecord.transaction do
package = ::Packages::CreateTemporaryPackageService.new(
- user_project, current_user, declared_params.merge(build: current_authenticated_job)
+ project, current_user, declared_params.merge(build: current_authenticated_job)
).execute(:rubygems, name: ::Packages::Rubygems::TEMPORARY_PACKAGE_NAME)
file_params = {
@@ -159,7 +165,7 @@ module API
bad_request!('Package creation failed')
end
rescue ObjectStorage::RemoteStoreError => e
- Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: user_project.id })
+ Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project.id })
forbidden!
end
@@ -179,13 +185,13 @@ module API
optional :gems, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma delimited gem names'
end
get 'dependencies' do
- authorize_read_package!
+ authorize_read_package!(project)
if params[:gems].blank?
status :ok
else
results = params[:gems].map do |gem_name|
- service_result = Packages::Rubygems::DependencyResolverService.new(user_project, current_user, gem_name: gem_name).execute
+ service_result = Packages::Rubygems::DependencyResolverService.new(project, current_user, gem_name: gem_name).execute
render_api_error!(service_result.message, service_result.http_status) if service_result.error?
service_result.payload
diff --git a/lib/api/search.rb b/lib/api/search.rb
index cf6a1385783..2204437f2ec 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -59,8 +59,13 @@ module API
end
def search(additional_params = {})
+ search_service = search_service(additional_params)
+ if search_service.global_search? && !search_service.global_search_enabled_for_scope?
+ forbidden!('Global Search is disabled for this scope')
+ end
+
@search_duration_s = Benchmark.realtime do
- @results = search_service(additional_params).search_objects(preload_method)
+ @results = search_service.search_objects(preload_method)
end
set_global_search_log_information(additional_params)
@@ -68,7 +73,7 @@ module API
Gitlab::Metrics::GlobalSearchSlis.record_apdex(
elapsed: @search_duration_s,
search_type: search_type(additional_params),
- search_level: search_service(additional_params).level,
+ search_level: search_service.level,
search_scope: search_scope
)
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 8b47604fe86..06b576a982b 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -85,9 +85,15 @@ module API
optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page'
optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)'
given housekeeping_enabled: ->(val) { val } do
- requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run."
- requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
- requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run."
+ optional :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run."
+ optional :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
+ optional :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run."
+
+ optional :housekeeping_optimize_repository_period, type: Integer, desc: "Number of Git pushes after which Gitaly is asked to optimize a repository."
+
+ # Requires either all three deprecated attributes (housekeeping_full_repack_period, housekeeping_gc_period, housekeeping_incremental_repack_period) or housekeeping_optimize_repository_period
+ all_or_none_of :housekeeping_full_repack_period, :housekeeping_gc_period, :housekeeping_incremental_repack_period
+ exactly_one_of :housekeeping_incremental_repack_period, :housekeeping_optimize_repository_period
end
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
@@ -188,6 +194,7 @@ module API
optional :jira_connect_application_key, type: String, desc: "Application ID of the OAuth application that should be used to authenticate with the GitLab.com for Jira Cloud app"
optional :jira_connect_proxy_url, type: String, desc: "URL of the GitLab instance that should be used as a proxy for the GitLab.com for Jira Cloud app"
optional :bulk_import_enabled, type: Boolean, desc: 'Enable migrating GitLab groups and projects by direct transfer'
+ optional :allow_runner_registration_token, type: Boolean, desc: 'Allow registering runners using a registration token'
Gitlab::SSHPublicKey.supported_types.each do |type|
optional :"#{type}_key_restriction",
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
index cda30dc957f..52e5ab30d06 100644
--- a/lib/api/subscriptions.rb
+++ b/lib/api/subscriptions.rb
@@ -15,7 +15,7 @@ module API
entity: Entities::MergeRequest,
source: Project,
finder: ->(id) { find_merge_request_with_access(id, :update_merge_request) },
- feature_category: :code_review
+ feature_category: :code_review_workflow
},
{
type: 'issues',
diff --git a/lib/api/suggestions.rb b/lib/api/suggestions.rb
index 6260983087f..eee83d5655b 100644
--- a/lib/api/suggestions.rb
+++ b/lib/api/suggestions.rb
@@ -4,7 +4,7 @@ module API
class Suggestions < ::API::Base
before { authenticate! }
- feature_category :code_review
+ feature_category :code_review_workflow
resource :suggestions do
desc 'Apply suggestion patch in the Merge Request it was created' do
diff --git a/lib/api/users.rb b/lib/api/users.rb
index d2d45c94291..a9b09596728 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -62,6 +62,7 @@ module API
optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for user', documentation: { type: 'file' }
optional :theme_id, type: Integer, desc: 'The GitLab theme for the user'
optional :color_scheme_id, type: Integer, desc: 'The color scheme for the file viewer'
+ # TODO: Add `allow_blank: false` in 16.0. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/387005
optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
optional :note, type: String, desc: 'Admin note for this user'
optional :view_diffs_file_by_file, type: Boolean, desc: 'Flag indicating the user sees only one file diff per page'
@@ -294,6 +295,12 @@ module API
authenticated_as_admin!
params = declared_params(include_missing: false)
+
+ # TODO: Remove in 16.0. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/387005
+ if params.key?(:private_profile) && params[:private_profile].nil?
+ params[:private_profile] = Gitlab::CurrentSettings.user_defaults_to_private_profile
+ end
+
user = ::Users::AuthorizedCreateService.new(current_user, params).execute
if user.persisted?
@@ -341,6 +348,12 @@ module API
.where.not(id: user.id).exists?
user_params = declared_params(include_missing: false)
+
+ # TODO: Remove in 16.0. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/387005
+ if user_params.key?(:private_profile) && user_params[:private_profile].nil?
+ user_params[:private_profile] = Gitlab::CurrentSettings.user_defaults_to_private_profile
+ end
+
admin_making_changes_for_another_user = (current_user != user)
if user_params[:password].present?
@@ -824,7 +837,8 @@ module API
elsif user.deactivated?
forbidden!('Deactivated users cannot be unblocked by the API')
else
- user.activate
+ result = ::Users::UnblockService.new(current_user).execute(user)
+ result.success?
end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -1020,6 +1034,25 @@ module API
end
end
+ helpers do
+ def set_user_status(include_missing_params:)
+ forbidden! unless can?(current_user, :update_user_status, current_user)
+
+ if ::Users::SetStatusService.new(current_user, declared_params(include_missing: include_missing_params)).execute
+ present current_user.status, with: Entities::UserStatus
+ else
+ render_validation_error!(current_user.status)
+ end
+ end
+
+ params :set_user_status_params do
+ optional :emoji, type: String, desc: "The emoji to set on the status"
+ optional :message, type: String, desc: "The status message to set"
+ optional :availability, type: String, desc: "The availability of user to set"
+ optional :clear_status_after, type: String, desc: "Automatically clear emoji, message and availability fields after a certain time", values: UserStatus::CLEAR_STATUS_QUICK_OPTIONS.keys
+ end
+ end
+
desc "Get the currently authenticated user's SSH keys" do
success Entities::SSHKey
end
@@ -1299,21 +1332,30 @@ module API
desc 'Set the status of the current user' do
success Entities::UserStatus
+ detail 'Any parameters that are not passed will be nullified.'
end
params do
- optional :emoji, type: String, desc: "The emoji to set on the status"
- optional :message, type: String, desc: "The status message to set"
- optional :availability, type: String, desc: "The availability of user to set"
- optional :clear_status_after, type: String, desc: "Automatically clear emoji, message and availability fields after a certain time", values: UserStatus::CLEAR_STATUS_QUICK_OPTIONS.keys
+ use :set_user_status_params
end
put "status", feature_category: :users do
- forbidden! unless can?(current_user, :update_user_status, current_user)
+ set_user_status(include_missing_params: true)
+ end
- if ::Users::SetStatusService.new(current_user, declared_params).execute
- present current_user.status, with: Entities::UserStatus
- else
- render_validation_error!(current_user.status)
+ desc 'Set the status of the current user' do
+ success Entities::UserStatus
+ detail 'Any parameters that are not passed will be ignored.'
+ end
+ params do
+ use :set_user_status_params
+ end
+ patch "status", feature_category: :users do
+ if declared_params(include_missing: false).empty?
+ status :ok
+
+ break
end
+
+ set_user_status(include_missing_params: false)
end
desc 'get the status of the current user' do
diff --git a/lib/api/validations/validators/bulk_imports.rb b/lib/api/validations/validators/bulk_imports.rb
new file mode 100644
index 00000000000..8d49607f64c
--- /dev/null
+++ b/lib/api/validations/validators/bulk_imports.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module API
+ module Validations
+ module Validators
+ module BulkImports
+ class DestinationSlugPath < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ unless params[attr_name] =~ Gitlab::Regex.group_path_regex # rubocop: disable Style/GuardClause
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "cannot start with a dash or forward slash, or end with a period or forward slash. " \
+ "It can only contain alphanumeric characters, periods, underscores, and dashes. " \
+ "E.g. 'destination_namespace' not 'destination/namespace'"
+ )
+ end
+ end
+ end
+
+ class DestinationNamespacePath < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ return if params[attr_name].blank?
+
+ unless params[attr_name] =~ Gitlab::Regex.bulk_import_namespace_path_regex # rubocop: disable Style/GuardClause
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "cannot start with a dash or forward slash, or end with a period or forward slash. " \
+ "It can only contain alphanumeric characters, periods, underscores, forward slashes " \
+ "and dashes. E.g. 'destination_namespace' or 'destination/namespace'"
+ )
+ end
+ end
+ end
+
+ class SourceFullPath < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ unless params[attr_name] =~ Gitlab::Regex.bulk_import_namespace_path_regex # rubocop: disable Style/GuardClause
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "must be a relative path and not include protocol, sub-domain, or domain information. " \
+ "E.g. 'source/full/path' not 'https://example.com/source/full/path'" \
+ )
+ end
+ end
+ end
+ end
+ end
+ end
+end