summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 08:43:02 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 08:43:02 +0000
commitd9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch)
tree2341ef426af70ad1e289c38036737e04b0aa5007 /lib/api
parentd6e514dd13db8947884cd58fe2a9c2a063400a9b (diff)
downloadgitlab-ce-d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb.tar.gz
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/api.rb25
-rw-r--r--lib/api/base.rb17
-rw-r--r--lib/api/bulk_imports.rb2
-rw-r--r--lib/api/ci/helpers/runner.rb8
-rw-r--r--lib/api/ci/resource_groups.rb52
-rw-r--r--lib/api/ci/runners.rb30
-rw-r--r--lib/api/composer_packages.rb4
-rw-r--r--lib/api/container_repositories.rb2
-rw-r--r--lib/api/entities/basic_project_details.rb4
-rw-r--r--lib/api/entities/ci/reset_token_result.rb (renamed from lib/api/entities/ci/reset_registration_token_result.rb)2
-rw-r--r--lib/api/entities/ci/resource_group.rb11
-rw-r--r--lib/api/entities/clusters/agent_authorization.rb2
-rw-r--r--lib/api/entities/environment_basic.rb2
-rw-r--r--lib/api/entities/feature_flag.rb4
-rw-r--r--lib/api/entities/feature_flag/detailed_legacy_scope.rb11
-rw-r--r--lib/api/entities/feature_flag/legacy_scope.rb16
-rw-r--r--lib/api/entities/group_detail.rb4
-rw-r--r--lib/api/entities/issuable_entity.rb2
-rw-r--r--lib/api/entities/namespace_basic.rb2
-rw-r--r--lib/api/entities/project.rb4
-rw-r--r--lib/api/entities/user.rb9
-rw-r--r--lib/api/error_tracking/client_keys.rb (renamed from lib/api/error_tracking_client_keys.rb)2
-rw-r--r--lib/api/error_tracking/collector.rb (renamed from lib/api/error_tracking_collector.rb)2
-rw-r--r--lib/api/error_tracking/project_settings.rb (renamed from lib/api/error_tracking.rb)2
-rw-r--r--lib/api/group_container_repositories.rb1
-rw-r--r--lib/api/group_export.rb2
-rw-r--r--lib/api/groups.rb2
-rw-r--r--lib/api/helm_packages.rb7
-rw-r--r--lib/api/helpers.rb15
-rw-r--r--lib/api/helpers/common_helpers.rb2
-rw-r--r--lib/api/helpers/container_registry_helpers.rb15
-rw-r--r--lib/api/helpers/integrations_helpers.rb4
-rw-r--r--lib/api/integrations.rb178
-rw-r--r--lib/api/internal/base.rb2
-rw-r--r--lib/api/internal/kubernetes.rb19
-rw-r--r--lib/api/issues.rb39
-rw-r--r--lib/api/maven_packages.rb3
-rw-r--r--lib/api/merge_requests.rb3
-rw-r--r--lib/api/notes.rb3
-rw-r--r--lib/api/project_container_repositories.rb2
-rw-r--r--lib/api/project_export.rb48
-rw-r--r--lib/api/project_import.rb1
-rw-r--r--lib/api/projects.rb7
-rw-r--r--lib/api/projects_relation_builder.rb2
-rw-r--r--lib/api/repositories.rb3
-rw-r--r--lib/api/services.rb173
-rw-r--r--lib/api/settings.rb1
-rw-r--r--lib/api/tags.rb2
-rw-r--r--lib/api/unleash.rb2
-rw-r--r--lib/api/users.rb7
-rw-r--r--lib/api/validations/validators/project_portable.rb21
51 files changed, 485 insertions, 298 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index d0d96858f61..a4d42c735cb 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -11,11 +11,12 @@ module API
COMMIT_ENDPOINT_REQUIREMENTS = NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(sha: NO_SLASH_URL_PART_REGEX).freeze
USER_REQUIREMENTS = { user_id: NO_SLASH_URL_PART_REGEX }.freeze
LOG_FILTERS = ::Rails.application.config.filter_parameters + [/^output$/]
+ LOG_FORMATTER = Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp.new
insert_before Grape::Middleware::Error,
GrapeLogging::Middleware::RequestLogger,
logger: Logger.new(LOG_FILENAME),
- formatter: Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp.new,
+ formatter: LOG_FORMATTER,
include: [
GrapeLogging::Loggers::FilterParameters.new(LOG_FILTERS),
Gitlab::GrapeLogging::Loggers::ClientEnvLogger.new,
@@ -49,16 +50,19 @@ module API
before do
coerce_nil_params_to_array!
- api_endpoint = env['api.endpoint']
+ api_endpoint = request.env[Grape::Env::API_ENDPOINT]
feature_category = api_endpoint.options[:for].try(:feature_category_for_app, api_endpoint).to_s
+ # remote_ip is added here and the ContextLogger so that the
+ # client_id field is set correctly, as the user object does not
+ # survive between multiple context pushes.
Gitlab::ApplicationContext.push(
user: -> { @current_user },
project: -> { @project },
namespace: -> { @group },
runner: -> { @current_runner || @runner },
- caller_id: api_endpoint.endpoint_id,
remote_ip: request.ip,
+ caller_id: api_endpoint.endpoint_id,
feature_category: feature_category
)
end
@@ -124,6 +128,11 @@ module API
handle_api_exception(exception)
end
+ rescue_from RateLimitedService::RateLimitedError do |exception|
+ exception.log_request(context.request, context.current_user)
+ rack_response({ 'message' => { 'error' => exception.message } }.to_json, 429, exception.headers)
+ end
+
format :json
formatter :json, Gitlab::Json::GrapeFormatter
content_type :json, 'application/json'
@@ -132,6 +141,7 @@ module API
helpers ::API::Helpers
helpers ::API::Helpers::CommonHelpers
helpers ::API::Helpers::PerformanceBarHelpers
+ helpers ::API::Helpers::RateLimiter
namespace do
after do
@@ -157,6 +167,7 @@ module API
mount ::API::Ci::Jobs
mount ::API::Ci::Pipelines
mount ::API::Ci::PipelineSchedules
+ mount ::API::Ci::ResourceGroups
mount ::API::Ci::Runner
mount ::API::Ci::Runners
mount ::API::Ci::Triggers
@@ -170,9 +181,9 @@ module API
mount ::API::DeployTokens
mount ::API::Deployments
mount ::API::Environments
- mount ::API::ErrorTracking
- mount ::API::ErrorTrackingClientKeys
- mount ::API::ErrorTrackingCollector
+ mount ::API::ErrorTracking::ClientKeys
+ mount ::API::ErrorTracking::Collector
+ mount ::API::ErrorTracking::ProjectSettings
mount ::API::Events
mount ::API::FeatureFlags
mount ::API::FeatureFlagsUserLists
@@ -259,7 +270,7 @@ module API
mount ::API::ResourceAccessTokens
mount ::API::RubygemPackages
mount ::API::Search
- mount ::API::Services
+ mount ::API::Integrations
mount ::API::Settings
mount ::API::SidekiqMetrics
mount ::API::SnippetRepositoryStorageMoves
diff --git a/lib/api/base.rb b/lib/api/base.rb
index 33e47c18fcd..c245a65b30b 100644
--- a/lib/api/base.rb
+++ b/lib/api/base.rb
@@ -2,20 +2,33 @@
module API
class Base < Grape::API::Instance # rubocop:disable API/Base
- include ::Gitlab::WithFeatureCategory
+ include ::Gitlab::EndpointAttributes
class << self
def feature_category_for_app(app)
feature_category_for_action(path_for_app(app))
end
+ def urgency_for_app(app)
+ urgency_for_action(path_for_app(app))
+ end
+
def path_for_app(app)
normalize_path(app.namespace, app.options[:path].first)
end
+ def endpoint_id_for_route(route)
+ "#{route.request_method} #{route.origin}"
+ end
+
def route(methods, paths = ['/'], route_options = {}, &block)
+ actions = Array(paths).map { |path| normalize_path(namespace, path) }
if category = route_options.delete(:feature_category)
- feature_category(category, Array(paths).map { |path| normalize_path(namespace, path) })
+ feature_category(category, actions)
+ end
+
+ if target = route_options.delete(:urgency)
+ urgency(target, actions)
end
super
diff --git a/lib/api/bulk_imports.rb b/lib/api/bulk_imports.rb
index 0705a8285c1..c732da17166 100644
--- a/lib/api/bulk_imports.rb
+++ b/lib/api/bulk_imports.rb
@@ -51,7 +51,7 @@ module API
end
end
post do
- response = BulkImportService.new(
+ response = ::BulkImports::CreateService.new(
current_user,
params[:entities],
url: params[:configuration][:url],
diff --git a/lib/api/ci/helpers/runner.rb b/lib/api/ci/helpers/runner.rb
index b9662b822fb..dabb6c7ab3a 100644
--- a/lib/api/ci/helpers/runner.rb
+++ b/lib/api/ci/helpers/runner.rb
@@ -42,8 +42,7 @@ module API
token = params[:token]
if token
- ::Gitlab::Database::LoadBalancing::RackMiddleware
- .stick_or_unstick(env, :runner, token)
+ ::Ci::Runner.sticking.stick_or_unstick_request(env, :runner, token)
end
strong_memoize(:current_runner) do
@@ -80,8 +79,9 @@ module API
id = params[:id]
if id
- ::Gitlab::Database::LoadBalancing::RackMiddleware
- .stick_or_unstick(env, :build, id)
+ ::Ci::Build
+ .sticking
+ .stick_or_unstick_request(env, :build, id)
end
strong_memoize(:current_job) do
diff --git a/lib/api/ci/resource_groups.rb b/lib/api/ci/resource_groups.rb
new file mode 100644
index 00000000000..616bec499d4
--- /dev/null
+++ b/lib/api/ci/resource_groups.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module API
+ module Ci
+ class ResourceGroups < ::API::Base
+ before { authenticate! }
+
+ feature_category :continuous_delivery
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'Get a single resource group' do
+ success Entities::Ci::ResourceGroup
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the resource group'
+ end
+ get ':id/resource_groups/:key' do
+ authorize! :read_resource_group, resource_group
+
+ present resource_group, with: Entities::Ci::ResourceGroup
+ end
+
+ desc 'Edit a resource group' do
+ success Entities::Ci::ResourceGroup
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the resource group'
+ optional :process_mode, type: String, desc: 'The process mode',
+ values: ::Ci::ResourceGroup.process_modes.keys
+ end
+ put ':id/resource_groups/:key' do
+ authorize! :update_resource_group, resource_group
+
+ if resource_group.update(declared_params(include_missing: false))
+ present resource_group, with: Entities::Ci::ResourceGroup
+ else
+ render_validation_error!(resource_group)
+ end
+ end
+ end
+
+ helpers do
+ def resource_group
+ @resource_group ||= user_project.resource_groups.find_by_key!(params[:key])
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 93a40925c21..ef712c84804 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -130,6 +130,20 @@ module API
present paginate(jobs), with: Entities::Ci::JobBasicWithProject
end
+
+ desc 'Reset runner authentication token' do
+ success Entities::Ci::ResetTokenResult
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the runner'
+ end
+ post ':id/reset_authentication_token' do
+ runner = get_runner(params[:id])
+ authenticate_update_runner!(runner)
+
+ runner.reset_token!
+ present runner.token, with: Entities::Ci::ResetTokenResult
+ end
end
params do
@@ -190,7 +204,7 @@ module API
not_found!('Runner') unless runner_project
runner = runner_project.runner
- forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
+ forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.runner_projects.count == 1
destroy_conditionally!(runner_project)
end
@@ -226,13 +240,13 @@ module API
before { authenticate_non_get! }
desc 'Resets runner registration token' do
- success Entities::Ci::ResetRegistrationTokenResult
+ success Entities::Ci::ResetTokenResult
end
post 'reset_registration_token' do
authorize! :update_runners_registration_token
ApplicationSetting.current.reset_runners_registration_token!
- present ApplicationSetting.current_without_cache.runners_registration_token, with: Entities::Ci::ResetRegistrationTokenResult
+ present ApplicationSetting.current_without_cache.runners_registration_token, with: Entities::Ci::ResetTokenResult
end
end
@@ -243,14 +257,14 @@ module API
before { authenticate_non_get! }
desc 'Resets runner registration token' do
- success Entities::Ci::ResetRegistrationTokenResult
+ success Entities::Ci::ResetTokenResult
end
post ':id/runners/reset_registration_token' do
project = find_project! user_project.id
authorize! :update_runners_registration_token, project
project.reset_runners_token!
- present project.runners_token, with: Entities::Ci::ResetRegistrationTokenResult
+ present project.runners_token, with: Entities::Ci::ResetTokenResult
end
end
@@ -261,14 +275,14 @@ module API
before { authenticate_non_get! }
desc 'Resets runner registration token' do
- success Entities::Ci::ResetRegistrationTokenResult
+ success Entities::Ci::ResetTokenResult
end
post ':id/runners/reset_registration_token' do
group = find_group! user_group.id
authorize! :update_runners_registration_token, group
group.reset_runners_token!
- present group.runners_token, with: Entities::Ci::ResetRegistrationTokenResult
+ present group.runners_token, with: Entities::Ci::ResetTokenResult
end
end
@@ -317,7 +331,7 @@ module API
def authenticate_delete_runner!(runner)
return if current_user.admin?
- forbidden!("Runner associated with more than one project") if runner.projects.count > 1
+ forbidden!("Runner associated with more than one project") if runner.runner_projects.count > 1
forbidden!("No access granted") unless can?(current_user, :delete_runner, runner)
end
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index 7b3750b37ee..94cad7e6c65 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -137,12 +137,12 @@ module API
bad_request!
end
- track_package_event('push_package', :composer, project: authorized_user_project, user: current_user, namespace: authorized_user_project.namespace)
-
::Packages::Composer::CreatePackageService
.new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job))
.execute
+ track_package_event('push_package', :composer, project: authorized_user_project, user: current_user, namespace: authorized_user_project.namespace)
+
created!
end
diff --git a/lib/api/container_repositories.rb b/lib/api/container_repositories.rb
index c84527f26e7..9cd3e449687 100644
--- a/lib/api/container_repositories.rb
+++ b/lib/api/container_repositories.rb
@@ -3,6 +3,8 @@
module API
class ContainerRepositories < ::API::Base
include Gitlab::Utils::StrongMemoize
+ include ::API::Helpers::ContainerRegistryHelpers
+
helpers ::API::Helpers::PackagesHelpers
before { authenticate! }
diff --git a/lib/api/entities/basic_project_details.rb b/lib/api/entities/basic_project_details.rb
index 5c33af86b84..e96504db53e 100644
--- a/lib/api/entities/basic_project_details.rb
+++ b/lib/api/entities/basic_project_details.rb
@@ -39,11 +39,11 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {})
# Preloading topics, should be done with using only `:topics`,
- # as `:topics` are defined as: `has_many :topics, through: :taggings`
+ # as `:topics` are defined as: `has_many :topics, through: :project_topics`
# N+1 is solved then by using `subject.topics.map(&:name)`
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555
projects_relation.preload(:project_feature, :route)
- .preload(:import_state, :topics, :topics_acts_as_taggable)
+ .preload(:import_state, :topics)
.preload(:auto_devops)
.preload(namespace: [:route, :owner])
end
diff --git a/lib/api/entities/ci/reset_registration_token_result.rb b/lib/api/entities/ci/reset_token_result.rb
index 23426432f68..4dbf831582b 100644
--- a/lib/api/entities/ci/reset_registration_token_result.rb
+++ b/lib/api/entities/ci/reset_token_result.rb
@@ -3,7 +3,7 @@
module API
module Entities
module Ci
- class ResetRegistrationTokenResult < Grape::Entity
+ class ResetTokenResult < Grape::Entity
expose(:token) {|object| object}
end
end
diff --git a/lib/api/entities/ci/resource_group.rb b/lib/api/entities/ci/resource_group.rb
new file mode 100644
index 00000000000..0afadfa9e2a
--- /dev/null
+++ b/lib/api/entities/ci/resource_group.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ class ResourceGroup < Grape::Entity
+ expose :id, :key, :process_mode, :created_at, :updated_at
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/clusters/agent_authorization.rb b/lib/api/entities/clusters/agent_authorization.rb
index 6c533fff105..7bbe0f1ec45 100644
--- a/lib/api/entities/clusters/agent_authorization.rb
+++ b/lib/api/entities/clusters/agent_authorization.rb
@@ -5,7 +5,7 @@ module API
module Clusters
class AgentAuthorization < Grape::Entity
expose :agent_id, as: :id
- expose :project, with: Entities::ProjectIdentity, as: :config_project
+ expose :config_project, with: Entities::ProjectIdentity
expose :config, as: :configuration
end
end
diff --git a/lib/api/entities/environment_basic.rb b/lib/api/entities/environment_basic.rb
index 061d4739874..d9894eac147 100644
--- a/lib/api/entities/environment_basic.rb
+++ b/lib/api/entities/environment_basic.rb
@@ -3,7 +3,7 @@
module API
module Entities
class EnvironmentBasic < Grape::Entity
- expose :id, :name, :slug, :external_url
+ expose :id, :name, :slug, :external_url, :created_at, :updated_at
end
end
end
diff --git a/lib/api/entities/feature_flag.rb b/lib/api/entities/feature_flag.rb
index f383eabd5dc..9dec3873504 100644
--- a/lib/api/entities/feature_flag.rb
+++ b/lib/api/entities/feature_flag.rb
@@ -9,7 +9,9 @@ module API
expose :version
expose :created_at
expose :updated_at
- expose :scopes, using: FeatureFlag::LegacyScope
+ expose :scopes do |_ff|
+ []
+ end
expose :strategies, using: FeatureFlag::Strategy
end
end
diff --git a/lib/api/entities/feature_flag/detailed_legacy_scope.rb b/lib/api/entities/feature_flag/detailed_legacy_scope.rb
deleted file mode 100644
index 47078c1dfde..00000000000
--- a/lib/api/entities/feature_flag/detailed_legacy_scope.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- class FeatureFlag < Grape::Entity
- class DetailedLegacyScope < LegacyScope
- expose :name
- end
- end
- end
-end
diff --git a/lib/api/entities/feature_flag/legacy_scope.rb b/lib/api/entities/feature_flag/legacy_scope.rb
deleted file mode 100644
index 7329f71c599..00000000000
--- a/lib/api/entities/feature_flag/legacy_scope.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- class FeatureFlag < Grape::Entity
- class LegacyScope < Grape::Entity
- expose :id
- expose :active
- expose :environment_scope
- expose :strategies
- expose :created_at
- expose :updated_at
- end
- end
- end
-end
diff --git a/lib/api/entities/group_detail.rb b/lib/api/entities/group_detail.rb
index 61f35d0f784..5eaccbc7154 100644
--- a/lib/api/entities/group_detail.rb
+++ b/lib/api/entities/group_detail.rb
@@ -16,7 +16,7 @@ module API
options: { only_owned: true, limit: projects_limit }
).execute
- Entities::Project.prepare_relation(projects)
+ Entities::Project.prepare_relation(projects, options)
end
expose :shared_projects, using: Entities::Project do |group, options|
@@ -26,7 +26,7 @@ module API
options: { only_shared: true, limit: projects_limit }
).execute
- Entities::Project.prepare_relation(projects)
+ Entities::Project.prepare_relation(projects, options)
end
def projects_limit
diff --git a/lib/api/entities/issuable_entity.rb b/lib/api/entities/issuable_entity.rb
index fd5d6c8137f..e2c674c0b8b 100644
--- a/lib/api/entities/issuable_entity.rb
+++ b/lib/api/entities/issuable_entity.rb
@@ -24,7 +24,7 @@ module API
# entity according to the current top-level entity options, such
# as the current_user.
def lazy_issuable_metadata
- BatchLoader.for(object).batch(key: [current_user, :issuable_metadata], replace_methods: false) do |models, loader, args|
+ BatchLoader.for(object).batch(key: [current_user, :issuable_metadata]) do |models, loader, args|
current_user = args[:key].first
issuable_metadata = Gitlab::IssuableMetadata.new(current_user, models)
diff --git a/lib/api/entities/namespace_basic.rb b/lib/api/entities/namespace_basic.rb
index f968a074bd2..2b9dd0b5f4d 100644
--- a/lib/api/entities/namespace_basic.rb
+++ b/lib/api/entities/namespace_basic.rb
@@ -6,7 +6,7 @@ module API
expose :id, :name, :path, :kind, :full_path, :parent_id, :avatar_url
expose :web_url do |namespace|
- if namespace.user?
+ if namespace.user_namespace?
Gitlab::Routing.url_helpers.user_url(namespace.owner)
else
namespace.web_url
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index b0e53ac3794..df0c1d7a4c5 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -132,7 +132,7 @@ module API
def self.preload_relation(projects_relation, options = {})
# Preloading topics, should be done with using only `:topics`,
- # as `:topics` are defined as: `has_many :topics, through: :taggings`
+ # as `:topics` are defined as: `has_many :topics, through: :project_topics`
# N+1 is solved then by using `subject.topics.map(&:name)`
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555
super(projects_relation).preload(group: :namespace_settings)
@@ -144,7 +144,7 @@ module API
.preload(project_group_links: { group: :route },
fork_network: :root_project,
fork_network_member: :forked_from_project,
- forked_from_project: [:route, :topics, :topics_acts_as_taggable, :group, :project_feature, namespace: [:route, :owner]])
+ forked_from_project: [:route, :topics, :group, :project_feature, namespace: [:route, :owner]])
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/entities/user.rb b/lib/api/entities/user.rb
index 5c46233a639..ff711b4dec2 100644
--- a/lib/api/entities/user.rb
+++ b/lib/api/entities/user.rb
@@ -4,7 +4,7 @@ module API
module Entities
class User < UserBasic
include UsersHelper
- include ActionView::Helpers::SanitizeHelper
+ include TimeZoneHelper
expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
expose :bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title, :pronouns
@@ -18,11 +18,8 @@ module API
expose :following, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } do |user|
user.followees.size
end
-
- # This is only for multi version compatibility reasons, as we removed user.bio_html
- # to be removed in 14.4
- expose :bio_html do |user|
- strip_tags(user.bio)
+ expose :local_time do |user|
+ local_time(user.timezone)
end
end
end
diff --git a/lib/api/error_tracking_client_keys.rb b/lib/api/error_tracking/client_keys.rb
index eaa84b7186c..e97df03b6f0 100644
--- a/lib/api/error_tracking_client_keys.rb
+++ b/lib/api/error_tracking/client_keys.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ErrorTrackingClientKeys < ::API::Base
+ class ErrorTracking::ClientKeys < ::API::Base
before { authenticate! }
feature_category :error_tracking
diff --git a/lib/api/error_tracking_collector.rb b/lib/api/error_tracking/collector.rb
index b1e0f6a858a..22fbd3a1118 100644
--- a/lib/api/error_tracking_collector.rb
+++ b/lib/api/error_tracking/collector.rb
@@ -4,7 +4,7 @@ module API
# This API is responsible for collecting error tracking information
# from sentry client. It allows us to use GitLab as an alternative to
# sentry backend. For more details see https://gitlab.com/gitlab-org/gitlab/-/issues/329596.
- class ErrorTrackingCollector < ::API::Base
+ class ErrorTracking::Collector < ::API::Base
feature_category :error_tracking
content_type :envelope, 'application/x-sentry-envelope'
diff --git a/lib/api/error_tracking.rb b/lib/api/error_tracking/project_settings.rb
index 369efe3bf8c..74432d1eaec 100644
--- a/lib/api/error_tracking.rb
+++ b/lib/api/error_tracking/project_settings.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ErrorTracking < ::API::Base
+ class ErrorTracking::ProjectSettings < ::API::Base
before { authenticate! }
feature_category :error_tracking
diff --git a/lib/api/group_container_repositories.rb b/lib/api/group_container_repositories.rb
index 96175f31696..55e18fd1370 100644
--- a/lib/api/group_container_repositories.rb
+++ b/lib/api/group_container_repositories.rb
@@ -3,6 +3,7 @@
module API
class GroupContainerRepositories < ::API::Base
include PaginationParams
+ include ::API::Helpers::ContainerRegistryHelpers
helpers ::API::Helpers::PackagesHelpers
diff --git a/lib/api/group_export.rb b/lib/api/group_export.rb
index 7e4fdba6033..25cc4e53bd2 100644
--- a/lib/api/group_export.rb
+++ b/lib/api/group_export.rb
@@ -2,8 +2,6 @@
module API
class GroupExport < ::API::Base
- helpers Helpers::RateLimiter
-
before do
not_found! unless Feature.enabled?(:group_import_export, user_group, default_enabled: true)
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index a1123b6291b..680e3a6e994 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -92,7 +92,7 @@ module API
projects, options = with_custom_attributes(projects, options)
- present options[:with].prepare_relation(projects), options
+ present options[:with].prepare_relation(projects, options), options
end
def present_groups(params, groups)
diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb
index 8a7e84c9f87..4278d17e003 100644
--- a/lib/api/helm_packages.rb
+++ b/lib/api/helm_packages.rb
@@ -11,7 +11,8 @@ module API
feature_category :package_registry
PACKAGE_FILENAME = 'package.tgz'
- FILE_NAME_REQUIREMENTS = {
+ HELM_REQUIREMENTS = {
+ channel: API::NO_SLASH_URL_PART_REGEX,
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
@@ -33,7 +34,7 @@ module API
requires :id, type: String, desc: 'The ID or full path of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- namespace ':id/packages/helm' do
+ namespace ':id/packages/helm', requirements: HELM_REQUIREMENTS do
desc 'Download a chart index' do
detail 'This feature was introduced in GitLab 14.0'
end
@@ -58,7 +59,7 @@ module API
requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
requires :file_name, type: String, desc: 'Helm package file name'
end
- get ":channel/charts/:file_name.tgz", requirements: FILE_NAME_REQUIREMENTS do
+ get ":channel/charts/:file_name.tgz" do
authorize_read_package!(authorized_user_project)
package_file = Packages::Helm::PackageFilesFinder.new(authorized_user_project, params[:channel], file_name: "#{params[:file_name]}.tgz").most_recent!
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 9c347148fd0..f9ba5ba8186 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -75,8 +75,9 @@ module API
save_current_user_in_env(@current_user) if @current_user
if @current_user
- ::Gitlab::Database::LoadBalancing::RackMiddleware
- .stick_or_unstick(env, :user, @current_user.id)
+ ::ApplicationRecord
+ .sticking
+ .stick_or_unstick_request(env, :user, @current_user.id)
end
@current_user
@@ -429,8 +430,8 @@ module API
render_api_error!('406 Not Acceptable', 406)
end
- def service_unavailable!
- render_api_error!('503 Service Unavailable', 503)
+ def service_unavailable!(message = nil)
+ render_api_error!(message || '503 Service Unavailable', 503)
end
def conflict!(message = nil)
@@ -624,6 +625,12 @@ module API
{}
end
+ def validate_anonymous_search_access!
+ return if current_user.present? || Feature.disabled?(:disable_anonymous_search, type: :ops)
+
+ unprocessable_entity!('User must be authenticated to use search')
+ end
+
private
# rubocop:disable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/api/helpers/common_helpers.rb b/lib/api/helpers/common_helpers.rb
index 02942820982..855648f2ef0 100644
--- a/lib/api/helpers/common_helpers.rb
+++ b/lib/api/helpers/common_helpers.rb
@@ -34,7 +34,7 @@ module API
end
def endpoint_id
- "#{request.request_method} #{route.origin}"
+ ::API::Base.endpoint_id_for_route(route)
end
end
end
diff --git a/lib/api/helpers/container_registry_helpers.rb b/lib/api/helpers/container_registry_helpers.rb
new file mode 100644
index 00000000000..9c844e364eb
--- /dev/null
+++ b/lib/api/helpers/container_registry_helpers.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module ContainerRegistryHelpers
+ extend ActiveSupport::Concern
+
+ included do
+ rescue_from Faraday::Error, ContainerRegistry::Path::InvalidRegistryPathError do |e|
+ service_unavailable!('We are having trouble connecting to the Container Registry. If this error persists, please review the troubleshooting documentation.')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index 06539772568..e0ef9099104 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -2,7 +2,7 @@
module API
module Helpers
- # Helpers module for API::Services
+ # Helpers module for API::Integrations
#
# The data structures inside this model are returned using class methods,
# allowing EE to extend them where necessary.
@@ -340,7 +340,7 @@ module API
required: true,
name: :webhook,
type: String,
- desc: 'Discord webhook. e.g. https://discordapp.com/api/webhooks/…'
+ desc: 'Discord webhook. For example, https://discord.com/api/webhooks/…'
}
],
'drone-ci' => [
diff --git a/lib/api/integrations.rb b/lib/api/integrations.rb
new file mode 100644
index 00000000000..926cde340a0
--- /dev/null
+++ b/lib/api/integrations.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+module API
+ class Integrations < ::API::Base
+ feature_category :integrations
+
+ integrations = Helpers::IntegrationsHelpers.integrations
+ integration_classes = Helpers::IntegrationsHelpers.integration_classes
+
+ if Rails.env.development?
+ integrations['mock-ci'] = [
+ {
+ required: true,
+ name: :mock_service_url,
+ type: String,
+ desc: 'URL to the mock integration'
+ }
+ ]
+ integrations['mock-deployment'] = []
+ integrations['mock-monitoring'] = []
+
+ integration_classes += Helpers::IntegrationsHelpers.development_integration_classes
+ end
+
+ INTEGRATIONS = integrations.freeze
+
+ integration_classes.each do |integration|
+ event_names = integration.try(:event_names) || next
+ event_names.each do |event_name|
+ INTEGRATIONS[integration.to_param.tr("_", "-")] << {
+ required: false,
+ name: event_name.to_sym,
+ type: String,
+ desc: IntegrationsHelper.integration_event_description(integration, event_name)
+ }
+ end
+ end
+
+ TRIGGER_INTEGRATIONS = {
+ 'mattermost-slash-commands' => [
+ {
+ name: :token,
+ type: String,
+ desc: 'The Mattermost token'
+ }
+ ],
+ 'slack-slash-commands' => [
+ {
+ name: :token,
+ type: String,
+ desc: 'The Slack token'
+ }
+ ]
+ }.freeze
+
+ helpers do
+ def integration_attributes(integration)
+ integration.fields.inject([]) do |arr, hash|
+ arr << hash[:name].to_sym
+ end
+ end
+ end
+
+ # The API officially documents only the `:id/integrations` API paths.
+ # We support the older `id:/services` path for backwards-compatibility in API V4.
+ # The support for `:id/services` can be dropped if we create an API V5.
+ [':id/services', ':id/integrations'].each do |path|
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before { authenticate! }
+ before { authorize_admin_project }
+
+ desc 'Get all active project integrations' do
+ success Entities::ProjectIntegrationBasic
+ end
+ get path do
+ integrations = user_project.integrations.active
+
+ present integrations, with: Entities::ProjectIntegrationBasic
+ end
+
+ INTEGRATIONS.each do |slug, settings|
+ desc "Set #{slug} integration for project"
+ params do
+ settings.each do |setting|
+ if setting[:required]
+ requires setting[:name], type: setting[:type], desc: setting[:desc]
+ else
+ optional setting[:name], type: setting[:type], desc: setting[:desc]
+ end
+ end
+ end
+ put "#{path}/#{slug}" do
+ integration = user_project.find_or_initialize_integration(slug.underscore)
+ params = declared_params(include_missing: false).merge(active: true)
+
+ if integration.update(params)
+ present integration, with: Entities::ProjectIntegration
+ else
+ render_api_error!('400 Bad Request', 400)
+ end
+ end
+ end
+
+ desc "Delete an integration from a project"
+ params do
+ requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the integration'
+ end
+ delete "#{path}/:slug" do
+ integration = user_project.find_or_initialize_integration(params[:slug].underscore)
+
+ destroy_conditionally!(integration) do
+ attrs = integration_attributes(integration).index_with { nil }.merge(active: false)
+
+ render_api_error!('400 Bad Request', 400) unless integration.update(attrs)
+ end
+ end
+
+ desc 'Get the integration settings for a project' do
+ success Entities::ProjectIntegration
+ end
+ params do
+ requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the integration'
+ end
+ get "#{path}/:slug" do
+ integration = user_project.find_or_initialize_integration(params[:slug].underscore)
+
+ not_found!('Integration') unless integration&.persisted?
+
+ present integration, with: Entities::ProjectIntegration
+ end
+ end
+
+ TRIGGER_INTEGRATIONS.each do |integration_slug, settings|
+ helpers do
+ def slash_command_integration(project, integration_slug, params)
+ project.integrations.active.find do |integration|
+ integration.try(:token) == params[:token] && integration.to_param == integration_slug.underscore
+ end
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc "Trigger a slash command for #{integration_slug}" do
+ detail 'Added in GitLab 8.13'
+ end
+ params do
+ settings.each do |setting|
+ requires setting[:name], type: setting[:type], desc: setting[:desc]
+ end
+ end
+ post "#{path}/#{integration_slug.underscore}/trigger" do
+ project = find_project(params[:id])
+
+ # This is not accurate, but done to prevent leakage of the project names
+ not_found!('Integration') unless project
+
+ integration = slash_command_integration(project, integration_slug, params)
+ result = integration.try(:trigger, params)
+
+ if result
+ status result[:status] || 200
+ present result
+ else
+ not_found!('Integration')
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+API::Integrations.prepend_mod_with('API::Integrations')
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index d740c626557..dc9257ebd62 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -164,7 +164,7 @@ module API
#
# Check whether an SSH key is known to GitLab
#
- get '/authorized_keys', feature_category: :source_code_management do
+ get '/authorized_keys', feature_category: :source_code_management, urgency: :high do
fingerprint = Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint_sha256
key = Key.find_by_fingerprint_sha256(fingerprint)
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index d1ad3c1feb1..f3974236fe3 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -79,25 +79,6 @@ module API
gitaly_repository: gitaly_repository(project)
}
end
-
- desc 'Gets project info' do
- detail 'Retrieves project info (if authorized)'
- end
- route_setting :authentication, cluster_agent_token_allowed: true
- get '/project_info' do
- project = find_project(params[:id])
-
- unless Guest.can?(:download_code, project) || agent.has_access_to?(project)
- not_found!
- end
-
- status 200
- {
- project_id: project.id,
- gitaly_info: gitaly_info(project),
- gitaly_repository: gitaly_repository(project)
- }
- end
end
namespace 'kubernetes/agent_configuration' do
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 39ce6e0b062..43e83bd58fe 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -4,7 +4,6 @@ module API
class Issues < ::API::Base
include PaginationParams
helpers Helpers::IssuesHelpers
- helpers Helpers::RateLimiter
before { authenticate_non_get! }
@@ -114,6 +113,7 @@ module API
end
get '/issues_statistics' do
authenticate! unless params[:scope] == 'all'
+ validate_anonymous_search_access! if params[:search].present?
present issues_statistics, with: Grape::Presenters::Presenter
end
@@ -131,6 +131,7 @@ module API
end
get do
authenticate! unless params[:scope] == 'all'
+ validate_anonymous_search_access! if params[:search].present?
issues = paginate(find_issues)
options = {
@@ -169,6 +170,7 @@ module API
optional :non_archived, type: Boolean, desc: 'Return issues from non archived projects', default: true
end
get ":id/issues" do
+ validate_anonymous_search_access! if declared_params[:search].present?
issues = paginate(find_issues(group_id: user_group.id, include_subgroups: true))
options = {
@@ -187,6 +189,8 @@ module API
use :issues_stats_params
end
get ":id/issues_statistics" do
+ validate_anonymous_search_access! if declared_params[:search].present?
+
present issues_statistics(group_id: user_group.id, include_subgroups: true), with: Grape::Presenters::Presenter
end
end
@@ -204,6 +208,7 @@ module API
use :issues_params
end
get ":id/issues" do
+ validate_anonymous_search_access! if declared_params[:search].present?
issues = paginate(find_issues(project_id: user_project.id))
options = {
@@ -222,6 +227,8 @@ module API
use :issues_stats_params
end
get ":id/issues_statistics" do
+ validate_anonymous_search_access! if declared_params[:search].present?
+
present issues_statistics(project_id: user_project.id), with: Grape::Presenters::Presenter
end
@@ -255,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]
+ check_rate_limit! :issues_create, [current_user] if Feature.disabled?("rate_limited_service_issues_create", user_project, default_enabled: :yaml)
authorize! :create_issue, user_project
@@ -375,6 +382,34 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ desc 'Clone an existing issue' do
+ success Entities::Issue
+ end
+ params do
+ requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
+ requires :to_project_id, type: Integer, desc: 'The ID of the new project'
+ optional :with_notes, type: Boolean, desc: 'Clone issue with notes', default: false
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ post ':id/issues/:issue_iid/clone' do
+ Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/340252')
+
+ issue = user_project.issues.find_by(iid: params[:issue_iid])
+ not_found!('Issue') unless issue
+
+ target_project = Project.find_by(id: params[:to_project_id])
+ not_found!('Project') unless target_project
+
+ begin
+ issue = ::Issues::CloneService.new(project: user_project, current_user: current_user)
+ .execute(issue, target_project, with_notes: params[:with_notes])
+ present issue, with: Entities::Issue, current_user: current_user, project: target_project
+ rescue ::Issues::CloneService::CloneError => error
+ render_api_error!(error.message, 400)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
desc 'Delete a project issue'
params do
requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index 9e5705abe88..5245cd10564 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -264,8 +264,6 @@ module API
when 'md5'
''
else
- track_package_event('push_package', :maven, user: current_user, project: user_project, namespace: user_project.namespace) if jar_file?(format)
-
file_params = {
file: params[:file],
size: params['file.size'],
@@ -276,6 +274,7 @@ module API
}
::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job)).execute
+ track_package_event('push_package', :maven, user: current_user, project: user_project, namespace: user_project.namespace) if jar_file?(format)
end
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 34af9eab511..21c1b7969aa 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -136,6 +136,7 @@ module API
end
get feature_category: :code_review do
authenticate! unless params[:scope] == 'all'
+ validate_anonymous_search_access! if params[:search].present?
merge_requests = find_merge_requests
present merge_requests, serializer_options_for(merge_requests)
@@ -155,6 +156,7 @@ module API
default: true
end
get ":id/merge_requests", feature_category: :code_review do
+ validate_anonymous_search_access! 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)
@@ -195,6 +197,7 @@ module API
end
get ":id/merge_requests", feature_category: :code_review do
authorize! :read_merge_request, user_project
+ validate_anonymous_search_access! if declared_params[:search].present?
merge_requests = find_merge_requests(project_id: user_project.id)
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 418efe3d1a7..656eaa2b2bb 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -4,7 +4,6 @@ module API
class Notes < ::API::Base
include PaginationParams
helpers ::API::Helpers::NotesHelpers
- helpers Helpers::RateLimiter
before { authenticate! }
@@ -88,7 +87,7 @@ module API
note = create_note(noteable, opts)
- if note.errors.keys == [:commands_only]
+ if note.errors.attribute_names == [:commands_only]
status 202
present note, with: Entities::NoteCommands
elsif note.valid?
diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb
index 28cfa9e3ae0..82b6082c3fe 100644
--- a/lib/api/project_container_repositories.rb
+++ b/lib/api/project_container_repositories.rb
@@ -3,6 +3,8 @@
module API
class ProjectContainerRepositories < ::API::Base
include PaginationParams
+ include ::API::Helpers::ContainerRegistryHelpers
+
helpers ::API::Helpers::PackagesHelpers
REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index 4041e130f9e..e01c195dbc4 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -2,8 +2,6 @@
module API
class ProjectExport < ::API::Base
- helpers Helpers::RateLimiter
-
feature_category :importers
before do
@@ -74,6 +72,52 @@ module API
accepted!
end
+
+ resource do
+ before do
+ not_found! unless ::Feature.enabled?(:bulk_import, default_enabled: :yaml)
+ end
+
+ desc 'Start relations export' do
+ detail 'This feature was introduced in GitLab 14.4'
+ end
+ post ':id/export_relations' do
+ response = ::BulkImports::ExportService.new(portable: user_project, user: current_user).execute
+
+ if response.success?
+ accepted!
+ else
+ render_api_error!(message: 'Project relations export could not be started.')
+ end
+ end
+
+ desc 'Download relations export' do
+ detail 'This feature was introduced in GitLab 14.4'
+ end
+ params do
+ requires :relation,
+ type: String,
+ project_portable: true,
+ desc: 'Project relation name'
+ end
+ get ':id/export_relations/download' do
+ export = user_project.bulk_import_exports.find_by_relation(params[:relation])
+ file = export&.upload&.export_file
+
+ 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 14.4'
+ end
+ get ':id/export_relations/status' do
+ present user_project.bulk_import_exports, with: Entities::BulkImports::ExportStatus
+ end
+ end
end
end
end
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index 039f7b4be41..d43184ff75d 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -6,7 +6,6 @@ module API
helpers Helpers::ProjectsHelpers
helpers Helpers::FileUploadHelpers
- helpers Helpers::RateLimiter
feature_category :importers
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 34e0b528ced..e8a48d6c9f4 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -182,8 +182,6 @@ module API
[options[:with].prepare_relation(projects, options), options]
end
- Preloaders::UserMaxAccessLevelInProjectsPreloader.new(records, current_user).execute if current_user
-
present records, options
end
@@ -658,10 +656,7 @@ module API
users = DeclarativePolicy.subject_scope { user_project.team.users }
users = users.search(params[:search]) if params[:search].present?
users = users.where_not_in(params[:skip_users]) if params[:skip_users].present?
-
- if Feature.enabled?(:sort_by_project_users_by_project_authorizations_user_id, user_project, default_enabled: :yaml)
- users = users.order('project_authorizations.user_id' => :asc) # rubocop: disable CodeReuse/ActiveRecord
- end
+ users = users.order('project_authorizations.user_id' => :asc) # rubocop: disable CodeReuse/ActiveRecord
present paginate(users), with: Entities::UserBasic
end
diff --git a/lib/api/projects_relation_builder.rb b/lib/api/projects_relation_builder.rb
index db46602cd90..a4bd06aec10 100644
--- a/lib/api/projects_relation_builder.rb
+++ b/lib/api/projects_relation_builder.rb
@@ -12,6 +12,8 @@ module API
preload_repository_cache(projects_relation)
+ Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects_relation, options[:current_user]).execute if options[:current_user]
+
projects_relation
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 3c9255e3117..1aa76906b3d 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -101,6 +101,7 @@ module API
params do
optional :sha, type: String, desc: 'The commit sha of the archive to be downloaded'
optional :format, type: String, desc: 'The archive format'
+ 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)
@@ -109,7 +110,7 @@ module API
not_acceptable! if Gitlab::HotlinkingDetector.intercept_hotlinking?(request)
- send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true
+ send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true, path: params[:path]
rescue StandardError
not_found!('File')
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
deleted file mode 100644
index a37b6f4626a..00000000000
--- a/lib/api/services.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-# frozen_string_literal: true
-module API
- class Services < ::API::Base
- feature_category :integrations
-
- integrations = Helpers::IntegrationsHelpers.integrations
- integration_classes = Helpers::IntegrationsHelpers.integration_classes
-
- if Rails.env.development?
- integrations['mock-ci'] = [
- {
- required: true,
- name: :mock_service_url,
- type: String,
- desc: 'URL to the mock service'
- }
- ]
- integrations['mock-deployment'] = []
- integrations['mock-monitoring'] = []
-
- integration_classes += Helpers::IntegrationsHelpers.development_integration_classes
- end
-
- INTEGRATIONS = integrations.freeze
-
- integration_classes.each do |integration|
- event_names = integration.try(:event_names) || next
- event_names.each do |event_name|
- INTEGRATIONS[integration.to_param.tr("_", "-")] << {
- required: false,
- name: event_name.to_sym,
- type: String,
- desc: IntegrationsHelper.integration_event_description(integration, event_name)
- }
- end
- end
-
- TRIGGER_INTEGRATIONS = {
- 'mattermost-slash-commands' => [
- {
- name: :token,
- type: String,
- desc: 'The Mattermost token'
- }
- ],
- 'slack-slash-commands' => [
- {
- name: :token,
- type: String,
- desc: 'The Slack token'
- }
- ]
- }.freeze
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before { authenticate! }
- before { authorize_admin_project }
-
- helpers do
- def integration_attributes(integration)
- integration.fields.inject([]) do |arr, hash|
- arr << hash[:name].to_sym
- end
- end
- end
-
- desc 'Get all active project integrations' do
- success Entities::ProjectIntegrationBasic
- end
- get ":id/services" do
- integrations = user_project.integrations.active
-
- present integrations, with: Entities::ProjectIntegrationBasic
- end
-
- INTEGRATIONS.each do |slug, settings|
- desc "Set #{slug} integration for project"
- params do
- settings.each do |setting|
- if setting[:required]
- requires setting[:name], type: setting[:type], desc: setting[:desc]
- else
- optional setting[:name], type: setting[:type], desc: setting[:desc]
- end
- end
- end
- put ":id/services/#{slug}" do
- integration = user_project.find_or_initialize_integration(slug.underscore)
- params = declared_params(include_missing: false).merge(active: true)
-
- if integration.update(params)
- present integration, with: Entities::ProjectIntegration
- else
- render_api_error!('400 Bad Request', 400)
- end
- end
- end
-
- desc "Delete an integration from a project"
- params do
- requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the service'
- end
- delete ":id/services/:slug" do
- integration = user_project.find_or_initialize_integration(params[:slug].underscore)
-
- destroy_conditionally!(integration) do
- attrs = integration_attributes(integration).index_with { nil }.merge(active: false)
-
- render_api_error!('400 Bad Request', 400) unless integration.update(attrs)
- end
- end
-
- desc 'Get the integration settings for a project' do
- success Entities::ProjectIntegration
- end
- params do
- requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the service'
- end
- get ":id/services/:slug" do
- integration = user_project.find_or_initialize_integration(params[:slug].underscore)
-
- not_found!('Service') unless integration&.persisted?
-
- present integration, with: Entities::ProjectIntegration
- end
- end
-
- TRIGGER_INTEGRATIONS.each do |integration_slug, settings|
- helpers do
- def slash_command_integration(project, integration_slug, params)
- project.integrations.active.find do |integration|
- integration.try(:token) == params[:token] && integration.to_param == integration_slug.underscore
- end
- end
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc "Trigger a slash command for #{integration_slug}" do
- detail 'Added in GitLab 8.13'
- end
- params do
- settings.each do |setting|
- requires setting[:name], type: setting[:type], desc: setting[:desc]
- end
- end
- post ":id/services/#{integration_slug.underscore}/trigger" do
- project = find_project(params[:id])
-
- # This is not accurate, but done to prevent leakage of the project names
- not_found!('Service') unless project
-
- integration = slash_command_integration(project, integration_slug, params)
- result = integration.try(:trigger, params)
-
- if result
- status result[:status] || 200
- present result
- else
- not_found!('Service')
- end
- end
- end
- end
- end
-end
-
-API::Services.prepend_mod_with('API::Services')
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 36f816ae638..12e1d21a00d 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -177,6 +177,7 @@ module API
optional :whats_new_variant, type: String, values: ApplicationSetting.whats_new_variants.keys, desc: "What's new variant, possible values: `all_tiers`, `current_tier`, and `disabled`."
optional :floc_enabled, type: Grape::API::Boolean, desc: 'Enable FloC (Federated Learning of Cohorts)'
optional :user_deactivation_emails_enabled, type: Boolean, desc: 'Send emails to users upon account deactivation'
+ optional :suggest_pipeline_enabled, type: Boolean, desc: 'Enable pipeline suggestion banner'
ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
optional :"#{type}_key_restriction",
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index 395aacced78..f018b421edd 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -24,7 +24,7 @@ module API
use :pagination
end
get ':id/repository/tags', feature_category: :source_code_management do
- tags = ::TagsFinder.new(user_project.repository,
+ tags, _ = ::TagsFinder.new(user_project.repository,
sort: "#{params[:order_by]}_#{params[:sort]}",
search: params[:search]).execute
diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb
index 37fe540cde1..6dadaf4fc54 100644
--- a/lib/api/unleash.rb
+++ b/lib/api/unleash.rb
@@ -30,7 +30,7 @@ module API
end
desc 'Get a list of features'
- get 'client/features' do
+ get 'client/features', urgency: :medium do
present :version, 1
present :features, feature_flags, with: ::API::Entities::UnleashFeature
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 944be990c2f..f16e1148618 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -795,7 +795,7 @@ module API
use :pagination
optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) impersonation_tokens'
end
- get feature_category :authentication_and_authorization do
+ get feature_category: :authentication_and_authorization do
present paginate(finder(declared_params(include_missing: false)).execute), with: Entities::ImpersonationToken
end
@@ -1058,6 +1058,10 @@ module API
params do
requires :user_id, type: String, desc: 'The ID or username of the user'
requires :credit_card_validated_at, type: DateTime, desc: 'The time when the user\'s credit card was validated'
+ requires :credit_card_expiration_month, type: Integer, desc: 'The month the credit card expires'
+ requires :credit_card_expiration_year, type: Integer, desc: 'The year the credit card expires'
+ requires :credit_card_holder_name, type: String, desc: 'The credit card holder name'
+ requires :credit_card_mask_number, type: String, desc: 'The last 4 digits of credit card number'
end
put ":user_id/credit_card_validation", feature_category: :users do
authenticated_as_admin!
@@ -1093,7 +1097,6 @@ module API
attrs = declared_params(include_missing: false)
service = ::UserPreferences::UpdateService.new(current_user, attrs).execute
-
if service.success?
present preferences, with: Entities::UserPreferences
else
diff --git a/lib/api/validations/validators/project_portable.rb b/lib/api/validations/validators/project_portable.rb
new file mode 100644
index 00000000000..3a7ea5ea71e
--- /dev/null
+++ b/lib/api/validations/validators/project_portable.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module API
+ module Validations
+ module Validators
+ class ProjectPortable < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ portable = params[attr_name]
+
+ portable_relations = ::BulkImports::FileTransfer.config_for(::Project.new).portable_relations
+ return if portable_relations.include?(portable)
+
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "is not portable"
+ )
+ end
+ end
+ end
+ end
+end