Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/deploy_keys.rb5
-rw-r--r--lib/api/discussions.rb2
-rw-r--r--lib/api/entities/bridge.rb9
-rw-r--r--lib/api/entities/container_registry.rb1
-rw-r--r--lib/api/entities/group_detail.rb3
-rw-r--r--lib/api/entities/merge_request_basic.rb8
-rw-r--r--lib/api/entities/project.rb4
-rw-r--r--lib/api/entities/releases/evidence.rb2
-rw-r--r--lib/api/entities/releases/link.rb1
-rw-r--r--lib/api/entities/resource_milestone_event.rb20
-rw-r--r--lib/api/entities/runner_details.rb7
-rw-r--r--lib/api/entities/shared_group_with_group.rb17
-rw-r--r--lib/api/entities/shared_group_with_project.rb (renamed from lib/api/entities/shared_group.rb)2
-rw-r--r--lib/api/entities/ssh_key.rb3
-rw-r--r--lib/api/entities/user_with_admin.rb3
-rw-r--r--lib/api/features.rb4
-rw-r--r--lib/api/group_container_repositories.rb3
-rw-r--r--lib/api/group_export.rb6
-rw-r--r--lib/api/group_import.rb9
-rw-r--r--lib/api/groups.rb57
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/api/helpers/issues_helpers.rb2
-rw-r--r--lib/api/helpers/notes_helpers.rb2
-rw-r--r--lib/api/helpers/projects_helpers.rb2
-rw-r--r--lib/api/helpers/runner.rb32
-rw-r--r--lib/api/helpers/services_helpers.rb12
-rw-r--r--lib/api/issues.rb9
-rw-r--r--lib/api/jobs.rb26
-rw-r--r--lib/api/lsif_data.rb40
-rw-r--r--lib/api/merge_requests.rb9
-rw-r--r--lib/api/project_container_repositories.rb9
-rw-r--r--lib/api/project_export.rb4
-rw-r--r--lib/api/project_import.rb5
-rw-r--r--lib/api/project_repository_storage_moves.rb57
-rw-r--r--lib/api/projects.rb4
-rw-r--r--lib/api/release/links.rb2
-rw-r--r--lib/api/releases.rb11
-rw-r--r--lib/api/repositories.rb4
-rw-r--r--lib/api/resource_label_events.rb5
-rw-r--r--lib/api/resource_milestone_events.rb54
-rw-r--r--lib/api/runner.rb18
-rw-r--r--lib/api/search.rb15
-rw-r--r--lib/api/settings.rb6
-rw-r--r--lib/api/suggestions.rb43
-rw-r--r--lib/api/terraform/state.rb10
-rw-r--r--lib/api/todos.rb10
-rw-r--r--lib/api/users.rb19
-rw-r--r--lib/api/validations/validators/file_path.rb2
-rw-r--r--lib/api/validations/validators/untrusted_regexp.rb19
-rw-r--r--lib/api/wikis.rb2
51 files changed, 448 insertions, 155 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index b8135539cda..fb67258f331 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -159,7 +159,6 @@ module API
mount ::API::Keys
mount ::API::Labels
mount ::API::Lint
- mount ::API::LsifData
mount ::API::Markdown
mount ::API::Members
mount ::API::MergeRequestDiffs
@@ -170,6 +169,7 @@ module API
mount ::API::Notes
mount ::API::Discussions
mount ::API::ResourceLabelEvents
+ mount ::API::ResourceMilestoneEvents
mount ::API::NotificationSettings
mount ::API::Pages
mount ::API::PagesDomains
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index e86bcc19b2b..11340e91aae 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -25,7 +25,8 @@ module API
get "deploy_keys" do
authenticated_as_admin!
- present paginate(DeployKey.all), with: Entities::SSHKey
+ deploy_keys = DeployKey.all.preload_users
+ present paginate(deploy_keys), with: Entities::SSHKey
end
params do
@@ -42,7 +43,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ":id/deploy_keys" do
- keys = user_project.deploy_keys_projects.preload(:deploy_key)
+ keys = user_project.deploy_keys_projects.preload(deploy_key: [:user])
present paginate(keys), with: Entities::DeployKeysProject
end
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 0dd1850e526..7b453ada41c 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -78,6 +78,8 @@ module API
optional :line_range, type: Hash, desc: 'Multi-line start and end' do
requires :start_line_code, type: String, desc: 'Start line code for multi-line note'
requires :end_line_code, type: String, desc: 'End line code for multi-line note'
+ requires :start_line_type, type: String, desc: 'Start line type for multi-line note'
+ requires :end_line_type, type: String, desc: 'End line type for multi-line note'
end
end
end
diff --git a/lib/api/entities/bridge.rb b/lib/api/entities/bridge.rb
new file mode 100644
index 00000000000..8f0ee69399a
--- /dev/null
+++ b/lib/api/entities/bridge.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class Bridge < Entities::JobBasic
+ expose :downstream_pipeline, with: Entities::PipelineBasic
+ end
+ end
+end
diff --git a/lib/api/entities/container_registry.rb b/lib/api/entities/container_registry.rb
index 6250f35c7cb..cff627ab50a 100644
--- a/lib/api/entities/container_registry.rb
+++ b/lib/api/entities/container_registry.rb
@@ -16,6 +16,7 @@ module API
expose :project_id
expose :location
expose :created_at
+ expose :tags_count, if: -> (_, options) { options[:tags_count] }
expose :tags, using: Tag, if: -> (_, options) { options[:tags] }
end
diff --git a/lib/api/entities/group_detail.rb b/lib/api/entities/group_detail.rb
index e03047a6e75..93dc41da81d 100644
--- a/lib/api/entities/group_detail.rb
+++ b/lib/api/entities/group_detail.rb
@@ -3,6 +3,9 @@
module API
module Entities
class GroupDetail < Group
+ expose :shared_with_groups do |group, options|
+ SharedGroupWithGroup.represent(group.shared_with_group_links.public_or_visible_to_user(group, options[:current_user]))
+ end
expose :runners_token, if: lambda { |group, options| options[:user_can_admin_group] }
expose :projects, using: Entities::Project do |group, options|
projects = GroupProjectsFinder.new(
diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb
index 1a89a83a619..1643f267938 100644
--- a/lib/api/entities/merge_request_basic.rb
+++ b/lib/api/entities/merge_request_basic.rb
@@ -6,19 +6,15 @@ module API
expose :merged_by, using: Entities::UserBasic do |merge_request, _options|
merge_request.metrics&.merged_by
end
-
expose :merged_at do |merge_request, _options|
merge_request.metrics&.merged_at
end
-
expose :closed_by, using: Entities::UserBasic do |merge_request, _options|
merge_request.metrics&.latest_closed_by
end
-
expose :closed_at do |merge_request, _options|
merge_request.metrics&.latest_closed_at
end
-
expose :title_html, if: -> (_, options) { options[:render_html] } do |entity|
MarkupHelper.markdown_field(entity, :title)
end
@@ -33,7 +29,6 @@ module API
merge_request.assignee
end
expose :author, :assignees, using: Entities::UserBasic
-
expose :source_project_id, :target_project_id
expose :labels do |merge_request, options|
if options[:with_labels_details]
@@ -85,11 +80,8 @@ module API
end
expose :squash
-
expose :task_completion_status
-
expose :cannot_be_merged?, as: :has_conflicts
-
expose :mergeable_discussions_state?, as: :blocking_discussions_resolved
end
end
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 39cd2d610e4..55a57501858 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -90,9 +90,10 @@ module API
expose :build_coverage_regex
expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
expose :shared_with_groups do |project, options|
- SharedGroup.represent(project.project_group_links, options)
+ SharedGroupWithProject.represent(project.project_group_links, options)
end
expose :only_allow_merge_if_pipeline_succeeds
+ expose :allow_merge_on_skipped_pipeline
expose :request_access_enabled
expose :only_allow_merge_if_all_discussions_are_resolved
expose :remove_source_branch_after_merge
@@ -119,6 +120,7 @@ module API
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555
super(projects_relation).preload(:group)
.preload(:ci_cd_settings)
+ .preload(:project_setting)
.preload(:container_expiration_policy)
.preload(:auto_devops)
.preload(project_group_links: { group: :route },
diff --git a/lib/api/entities/releases/evidence.rb b/lib/api/entities/releases/evidence.rb
index 25b2bf6bf6f..01603a71dbf 100644
--- a/lib/api/entities/releases/evidence.rb
+++ b/lib/api/entities/releases/evidence.rb
@@ -6,7 +6,7 @@ module API
class Evidence < Grape::Entity
include ::API::Helpers::Presentable
- expose :summary_sha, as: :sha
+ expose :sha
expose :filepath
expose :collected_at
end
diff --git a/lib/api/entities/releases/link.rb b/lib/api/entities/releases/link.rb
index f4edb83bd58..654df2e2caf 100644
--- a/lib/api/entities/releases/link.rb
+++ b/lib/api/entities/releases/link.rb
@@ -9,6 +9,7 @@ module API
expose :url
expose :direct_asset_url
expose :external?, as: :external
+ expose :link_type
def direct_asset_url
return object.url unless object.filepath
diff --git a/lib/api/entities/resource_milestone_event.rb b/lib/api/entities/resource_milestone_event.rb
new file mode 100644
index 00000000000..26dc6620cbe
--- /dev/null
+++ b/lib/api/entities/resource_milestone_event.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class ResourceMilestoneEvent < Grape::Entity
+ expose :id
+ expose :user, using: Entities::UserBasic
+ expose :created_at
+ expose :resource_type do |event, _options|
+ event.issuable.class.name
+ end
+ expose :resource_id do |event, _options|
+ event.issuable.id
+ end
+ expose :milestone, using: Entities::Milestone
+ expose :action
+ expose :state
+ end
+ end
+end
diff --git a/lib/api/entities/runner_details.rb b/lib/api/entities/runner_details.rb
index 1dd8543d595..0afe298ef64 100644
--- a/lib/api/entities/runner_details.rb
+++ b/lib/api/entities/runner_details.rb
@@ -11,13 +11,6 @@ module API
expose :version, :revision, :platform, :architecture
expose :contacted_at
- # Will be removed: https://gitlab.com/gitlab-org/gitlab/-/issues/217105
- expose(:token, if: ->(runner, options) do
- return false if ::Feature.enabled?(:hide_token_from_runners_api, default_enabled: true)
-
- options[:current_user].admin? || !runner.instance_type?
- end)
-
# rubocop: disable CodeReuse/ActiveRecord
expose :projects, with: Entities::BasicProjectDetails do |runner, options|
if options[:current_user].admin?
diff --git a/lib/api/entities/shared_group_with_group.rb b/lib/api/entities/shared_group_with_group.rb
new file mode 100644
index 00000000000..1ca879182eb
--- /dev/null
+++ b/lib/api/entities/shared_group_with_group.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class SharedGroupWithGroup < Grape::Entity
+ expose :shared_with_group_id, as: :group_id
+ expose :group_name do |group_link|
+ group_link.shared_with_group.name
+ end
+ expose :group_full_path do |group_link|
+ group_link.shared_with_group.full_path
+ end
+ expose :group_access, as: :group_access_level
+ expose :expires_at
+ end
+ end
+end
diff --git a/lib/api/entities/shared_group.rb b/lib/api/entities/shared_group_with_project.rb
index 862e73e07f0..d91bee31b04 100644
--- a/lib/api/entities/shared_group.rb
+++ b/lib/api/entities/shared_group_with_project.rb
@@ -2,7 +2,7 @@
module API
module Entities
- class SharedGroup < Grape::Entity
+ class SharedGroupWithProject < Grape::Entity
expose :group_id
expose :group_name do |group_link, options|
group_link.group.name
diff --git a/lib/api/entities/ssh_key.rb b/lib/api/entities/ssh_key.rb
index aae216173c7..e1554730cb6 100644
--- a/lib/api/entities/ssh_key.rb
+++ b/lib/api/entities/ssh_key.rb
@@ -3,7 +3,8 @@
module API
module Entities
class SSHKey < Grape::Entity
- expose :id, :title, :key, :created_at, :expires_at
+ expose :id, :title, :created_at, :expires_at
+ expose :publishable_key, as: :key
end
end
end
diff --git a/lib/api/entities/user_with_admin.rb b/lib/api/entities/user_with_admin.rb
index d3df12200ff..c225ade6eb6 100644
--- a/lib/api/entities/user_with_admin.rb
+++ b/lib/api/entities/user_with_admin.rb
@@ -4,8 +4,7 @@ module API
module Entities
class UserWithAdmin < UserPublic
expose :admin?, as: :is_admin
+ expose :note
end
end
end
-
-API::Entities::UserWithAdmin.prepend_if_ee('EE::API::Entities::UserWithAdmin', with_descendants: true)
diff --git a/lib/api/features.rb b/lib/api/features.rb
index f507919b055..3fb3fc92e42 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -61,7 +61,7 @@ module API
mutually_exclusive :key, :project
end
post ':name' do
- feature = Feature.get(params[:name])
+ feature = Feature.get(params[:name]) # rubocop:disable Gitlab/AvoidFeatureGet
targets = gate_targets(params)
value = gate_value(params)
key = gate_key(params)
@@ -92,7 +92,7 @@ module API
desc 'Remove the gate value for the given feature'
delete ':name' do
- Feature.get(params[:name]).remove
+ Feature.remove(params[:name])
no_content!
end
diff --git a/lib/api/group_container_repositories.rb b/lib/api/group_container_repositories.rb
index 7f95b411b36..d34317b5271 100644
--- a/lib/api/group_container_repositories.rb
+++ b/lib/api/group_container_repositories.rb
@@ -20,6 +20,7 @@ module API
params do
use :pagination
optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included'
+ optional :tags_count, type: Boolean, default: false, desc: 'Determines if the tags count should be included'
end
get ':id/registry/repositories' do
repositories = ContainerRepositoriesFinder.new(
@@ -28,7 +29,7 @@ module API
track_event('list_repositories')
- present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags]
+ present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count]
end
end
diff --git a/lib/api/group_export.rb b/lib/api/group_export.rb
index 8ca5dfa082e..d3010b6d147 100644
--- a/lib/api/group_export.rb
+++ b/lib/api/group_export.rb
@@ -2,6 +2,8 @@
module API
class GroupExport < Grape::API
+ helpers Helpers::RateLimiter
+
before do
not_found! unless Feature.enabled?(:group_import_export, user_group, default_enabled: true)
@@ -16,6 +18,8 @@ module API
detail 'This feature was introduced in GitLab 12.5.'
end
get ':id/export/download' do
+ check_rate_limit! :group_download_export, [current_user, user_group]
+
if user_group.export_file_exists?
present_carrierwave_file!(user_group.export_file)
else
@@ -27,6 +31,8 @@ module API
detail 'This feature was introduced in GitLab 12.5.'
end
post ':id/export' do
+ check_rate_limit! :group_export, [current_user]
+
export_service = ::Groups::ImportExport::ExportService.new(group: user_group, user: current_user)
if export_service.async_execute
diff --git a/lib/api/group_import.rb b/lib/api/group_import.rb
index ec51c2f44c3..afcbc24d3ce 100644
--- a/lib/api/group_import.rb
+++ b/lib/api/group_import.rb
@@ -2,8 +2,6 @@
module API
class GroupImport < Grape::API
- MAXIMUM_FILE_SIZE = 50.megabytes.freeze
-
helpers Helpers::FileUploadHelpers
helpers do
@@ -40,7 +38,10 @@ module API
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
- ImportExportUploader.workhorse_authorize(has_length: false, maximum_size: MAXIMUM_FILE_SIZE)
+ ImportExportUploader.workhorse_authorize(
+ has_length: false,
+ maximum_size: Gitlab::CurrentSettings.max_import_size.megabytes
+ )
end
desc 'Create a new group import' do
@@ -69,7 +70,7 @@ module API
group = ::Groups::CreateService.new(current_user, group_params).execute
if group.persisted?
- GroupImportWorker.perform_async(current_user.id, group.id) # rubocop:disable CodeReuse/Worker
+ ::Groups::ImportExport::ImportService.new(group: group, user: current_user).async_execute
accepted!
else
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 353c8b4b242..6e07bb46721 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -23,13 +23,20 @@ module API
optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
+ optional :top_level_only, type: Boolean, desc: 'Only include top level groups'
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
def find_groups(params, parent_id = nil)
find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
- find_params[:parent] = find_group!(parent_id) if parent_id
+
+ find_params[:parent] = if params[:top_level_only]
+ [nil]
+ elsif parent_id
+ find_group!(parent_id)
+ end
+
find_params[:all_available] =
find_params.fetch(:all_available, current_user&.can_read_all_resources?)
@@ -144,6 +151,7 @@ module API
end
group = create_group
+ group.preload_shared_group_links
if group.persisted?
present group, with: Entities::GroupDetail, current_user: current_user
@@ -168,6 +176,8 @@ module API
end
put ':id' do
group = find_group!(params[:id])
+ group.preload_shared_group_links
+
authorize! :admin_group, group
if update_group(group)
@@ -186,6 +196,7 @@ module API
end
get ":id" do
group = find_group!(params[:id])
+ group.preload_shared_group_links
options = {
with: params[:with_projects] ? Entities::GroupDetail : Entities::Group,
@@ -292,6 +303,7 @@ module API
post ":id/projects/:project_id", requirements: { project_id: /.+/ } do
authenticated_as_admin!
group = find_group!(params[:id])
+ group.preload_shared_group_links
project = find_project!(params[:project_id])
result = ::Projects::TransferService.new(project, current_user).execute(group)
@@ -301,6 +313,49 @@ module API
render_api_error!("Failed to transfer project #{project.errors.messages}", 400)
end
end
+
+ desc 'Share a group with a group' do
+ success Entities::GroupDetail
+ end
+ params do
+ requires :group_id, type: Integer, desc: 'The ID of the group to share'
+ requires :group_access, type: Integer, values: Gitlab::Access.all_values, desc: 'The group access level'
+ optional :expires_at, type: Date, desc: 'Share expiration date'
+ end
+ post ":id/share" do
+ shared_group = find_group!(params[:id])
+ shared_with_group = find_group!(params[:group_id])
+
+ group_link_create_params = {
+ shared_group_access: params[:group_access],
+ expires_at: params[:expires_at]
+ }
+
+ result = ::Groups::GroupLinks::CreateService.new(shared_with_group, current_user, group_link_create_params).execute(shared_group)
+ shared_group.preload_shared_group_links
+
+ if result[:status] == :success
+ present shared_group, with: Entities::GroupDetail, current_user: current_user
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
+ params do
+ requires :group_id, type: Integer, desc: 'The ID of the shared group'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ delete ":id/share/:group_id" do
+ shared_group = find_group!(params[:id])
+
+ link = shared_group.shared_with_group_links.find_by(shared_with_group_id: params[:group_id])
+ not_found!('Group Link') unless link
+
+ ::Groups::GroupLinks::DestroyService.new(shared_group, current_user).execute(link)
+
+ no_content!
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index c6f6dc255d4..bbdb45da3b1 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -437,7 +437,7 @@ module API
if report_exception?(exception)
define_params_for_grape_middleware
Gitlab::ErrorTracking.with_context(current_user) do
- Gitlab::ErrorTracking.track_exception(exception, params)
+ Gitlab::ErrorTracking.track_exception(exception)
end
end
diff --git a/lib/api/helpers/issues_helpers.rb b/lib/api/helpers/issues_helpers.rb
index e272b13f3ae..638b31cc7ba 100644
--- a/lib/api/helpers/issues_helpers.rb
+++ b/lib/api/helpers/issues_helpers.rb
@@ -24,6 +24,8 @@ module API
:discussion_locked,
:due_date,
:labels,
+ :add_labels,
+ :remove_labels,
:milestone_id,
:state_event,
:title
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index c85a38fc18b..f88624ed63e 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -133,7 +133,7 @@ module API
if resolved
parent = noteable_parent(noteable)
- ::Discussions::ResolveService.new(parent, current_user, merge_request: noteable).execute(discussion)
+ ::Discussions::ResolveService.new(parent, current_user, one_or_more_discussions: discussion).execute
else
discussion.unresolve!
end
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 5afdb34da97..8a115d42929 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -44,6 +44,7 @@ module API
optional :public_builds, type: Boolean, desc: 'Perform public builds'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
+ optional :allow_merge_on_skipped_pipeline, type: Boolean, desc: 'Allow to merge if pipeline is skipped'
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
optional :tag_list, type: Array[String], desc: 'The list of tags for a project'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
@@ -92,6 +93,7 @@ module API
def self.update_params_at_least_one_of
[
+ :allow_merge_on_skipped_pipeline,
:autoclose_referenced_issues,
:auto_devops_enabled,
:auto_devops_deploy_strategy,
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 1f1253c8542..293d7ed9a6a 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -3,6 +3,8 @@
module API
module Helpers
module Runner
+ include Gitlab::Utils::StrongMemoize
+
prepend_if_ee('EE::API::Helpers::Runner') # rubocop: disable Cop/InjectEnterpriseEditionModule
JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'
@@ -16,7 +18,7 @@ module API
forbidden! unless current_runner
current_runner
- .update_cached_info(get_runner_details_from_request)
+ .heartbeat(get_runner_details_from_request)
end
def get_runner_details_from_request
@@ -31,31 +33,35 @@ module API
end
def current_runner
- @runner ||= ::Ci::Runner.find_by_token(params[:token].to_s)
+ strong_memoize(:current_runner) do
+ ::Ci::Runner.find_by_token(params[:token].to_s)
+ end
end
- def validate_job!(job)
- not_found! unless job
+ def authenticate_job!(require_running: true)
+ job = current_job
- yield if block_given?
+ not_found! unless job
+ forbidden! unless job_token_valid?(job)
- project = job.project
- forbidden!('Project has been deleted!') if project.nil? || project.pending_delete?
+ forbidden!('Project has been deleted!') if job.project.nil? || job.project.pending_delete?
forbidden!('Job has been erased!') if job.erased?
- end
- def authenticate_job!
- job = current_job
+ if require_running
+ job_forbidden!(job, 'Job is not running') unless job.running?
+ end
- validate_job!(job) do
- forbidden! unless job_token_valid?(job)
+ if Gitlab::Ci::Features.job_heartbeats_runner?(job.project)
+ job.runner&.heartbeat(get_runner_ip)
end
job
end
def current_job
- @current_job ||= Ci::Build.find_by_id(params[:id])
+ strong_memoize(:current_job) do
+ Ci::Build.find_by_id(params[:id])
+ end
end
def job_token_valid?(job)
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 02e60ff5db5..3d6039cacaa 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -583,6 +583,18 @@ module API
name: :api_url,
type: String,
desc: 'Prometheus API Base URL, like http://prometheus.example.com/'
+ },
+ {
+ required: true,
+ name: :google_iap_audience_client_id,
+ type: String,
+ desc: 'Client ID of the IAP secured resource (looks like IAP_CLIENT_ID.apps.googleusercontent.com)'
+ },
+ {
+ required: true,
+ name: :google_iap_service_account_json,
+ type: String,
+ desc: 'Contents of the credentials.json file of your service account, like: { "type": "service_account", "project_id": ... }'
}
],
'pushover' => [
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index be50c3e0381..2374ac11f4a 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -5,7 +5,6 @@ module API
include PaginationParams
helpers Helpers::IssuesHelpers
helpers Helpers::RateLimiter
- helpers ::Gitlab::IssuableMetadata
before { authenticate_non_get! }
@@ -67,6 +66,8 @@ module API
optional :assignee_id, type: Integer, desc: '[Deprecated] The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
+ optional :add_labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
+ optional :remove_labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked"
@@ -106,7 +107,7 @@ module API
with: Entities::Issue,
with_labels_details: declared_params[:with_labels_details],
current_user: current_user,
- issuable_metadata: issuable_meta_data(issues, 'Issue', current_user),
+ issuable_metadata: Gitlab::IssuableMetadata.new(current_user, issues).data,
include_subscribed: false
}
@@ -132,7 +133,7 @@ module API
with: Entities::Issue,
with_labels_details: declared_params[:with_labels_details],
current_user: current_user,
- issuable_metadata: issuable_meta_data(issues, 'Issue', current_user),
+ issuable_metadata: Gitlab::IssuableMetadata.new(current_user, issues).data,
include_subscribed: false,
group: user_group
}
@@ -169,7 +170,7 @@ module API
with_labels_details: declared_params[:with_labels_details],
current_user: current_user,
project: user_project,
- issuable_metadata: issuable_meta_data(issues, 'Issue', current_user),
+ issuable_metadata: Gitlab::IssuableMetadata.new(current_user, issues).data,
include_subscribed: false
}
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index 59f0dbe8a9b..61a7fc107ef 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -70,6 +70,32 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ desc 'Get pipeline bridge jobs' do
+ success Entities::Bridge
+ end
+ params do
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ use :optional_scope
+ use :pagination
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ':id/pipelines/:pipeline_id/bridges' do
+ authorize!(:read_build, user_project)
+ pipeline = user_project.ci_pipelines.find(params[:pipeline_id])
+ authorize!(:read_pipeline, pipeline)
+
+ bridges = pipeline.bridges
+ bridges = filter_builds(bridges, params[:scope])
+ bridges = bridges.preload(
+ :metadata,
+ downstream_pipeline: [project: [:route, { namespace: :route }]],
+ project: [:namespace]
+ )
+
+ present paginate(bridges), with: Entities::Bridge
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
desc 'Get a specific job of a project' do
success Entities::Job
end
diff --git a/lib/api/lsif_data.rb b/lib/api/lsif_data.rb
deleted file mode 100644
index a673ccb4af0..00000000000
--- a/lib/api/lsif_data.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module API
- class LsifData < Grape::API
- MAX_FILE_SIZE = 10.megabytes
-
- before do
- not_found! if Feature.disabled?(:code_navigation, user_project)
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- requires :commit_id, type: String, desc: 'The ID of a commit'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- segment ':id/commits/:commit_id' do
- params do
- requires :paths, type: Array, desc: 'The paths of the files'
- end
- get 'lsif/info' do
- authorize! :download_code, user_project
-
- artifact =
- Ci::JobArtifact
- .with_file_types(['lsif'])
- .for_sha(params[:commit_id], @project.id)
- .last
-
- not_found! unless artifact
- authorize! :read_pipeline, artifact.job.pipeline
- file_too_large! if artifact.file.cached_size > MAX_FILE_SIZE
-
- service = ::Projects::LsifDataService.new(artifact.file, @project, params[:commit_id])
-
- params[:paths].to_h { |path| [path, service.execute(path)] }
- end
- end
- end
- end
-end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index ff4ad85115b..773a451d3a8 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -8,7 +8,6 @@ module API
before { authenticate_non_get! }
- helpers ::Gitlab::IssuableMetadata
helpers Helpers::MergeRequestsHelpers
# EE::API::MergeRequests would override the following helpers
@@ -92,10 +91,8 @@ module API
if params[:view] == 'simple'
options[:with] = Entities::MergeRequestSimple
else
- options[:issuable_metadata] = issuable_meta_data(merge_requests, 'MergeRequest', current_user)
- if Feature.enabled?(:mr_list_api_skip_merge_status_recheck, default_enabled: true)
- options[:skip_merge_status_recheck] = !declared_params[:with_merge_status_recheck]
- end
+ options[:issuable_metadata] = Gitlab::IssuableMetadata.new(current_user, merge_requests).data
+ options[:skip_merge_status_recheck] = !declared_params[:with_merge_status_recheck]
end
options
@@ -478,7 +475,7 @@ module API
squash_commit_message: params[:squash_commit_message],
should_remove_source_branch: params[:should_remove_source_branch],
sha: params[:sha] || merge_request.diff_head_sha
- )
+ ).compact
if immediately_mergeable
::MergeRequests::MergeService
diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb
index 555fd98b451..2a0099018d9 100644
--- a/lib/api/project_container_repositories.rb
+++ b/lib/api/project_container_repositories.rb
@@ -21,6 +21,7 @@ module API
params do
use :pagination
optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included'
+ optional :tags_count, type: Boolean, default: false, desc: 'Determines if the tags count should be included'
end
get ':id/registry/repositories' do
repositories = ContainerRepositoriesFinder.new(
@@ -29,7 +30,7 @@ module API
track_event( 'list_repositories')
- present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags]
+ present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count]
end
desc 'Delete repository' do
@@ -69,11 +70,11 @@ module API
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
- optional :name_regex_delete, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
- optional :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
+ optional :name_regex_delete, type: String, untrusted_regexp: true, desc: 'The tag name regexp to delete, specify .* to delete all'
+ optional :name_regex, type: String, untrusted_regexp: true, desc: 'The tag name regexp to delete, specify .* to delete all'
# require either name_regex (deprecated) or name_regex_delete, it is ok to have both
at_least_one_of :name_regex, :name_regex_delete
- optional :name_regex_keep, type: String, desc: 'The tag name regexp to retain'
+ optional :name_regex_keep, type: String, untrusted_regexp: true, desc: 'The tag name regexp to retain'
optional :keep_n, type: Integer, desc: 'Keep n of latest tags with matching name'
optional :older_than, type: String, desc: 'Delete older than: 1h, 1d, 1month'
end
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index 9fd9d13a20c..4b35f245b8c 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -25,7 +25,7 @@ module API
detail 'This feature was introduced in GitLab 10.6.'
end
get ':id/export/download' do
- check_rate_limit! :project_download_export, [current_user, :project_download_export, user_project]
+ check_rate_limit! :project_download_export, [current_user, user_project]
if user_project.export_file_exists?
present_carrierwave_file!(user_project.export_file)
@@ -45,7 +45,7 @@ module API
end
end
post ':id/export' do
- check_rate_limit! :project_export, [current_user, :project_export, user_project]
+ check_rate_limit! :project_export, [current_user]
project_export_params = declared_params(include_missing: false)
after_export_params = project_export_params.delete(:upload) || {}
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index 0e83686cab2..17d08d14a20 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -30,7 +30,10 @@ module API
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
- ImportExportUploader.workhorse_authorize(has_length: false, maximum_size: MAXIMUM_FILE_SIZE)
+ ImportExportUploader.workhorse_authorize(
+ has_length: false,
+ maximum_size: Gitlab::CurrentSettings.max_import_size.megabytes
+ )
end
params do
diff --git a/lib/api/project_repository_storage_moves.rb b/lib/api/project_repository_storage_moves.rb
index 1a63e984fbf..5de623102fb 100644
--- a/lib/api/project_repository_storage_moves.rb
+++ b/lib/api/project_repository_storage_moves.rb
@@ -24,11 +24,64 @@ module API
detail 'This feature was introduced in GitLab 13.0.'
success Entities::ProjectRepositoryStorageMove
end
- get ':id' do
- storage_move = ProjectRepositoryStorageMove.find(params[:id])
+ params do
+ requires :repository_storage_move_id, type: Integer, desc: 'The ID of a project repository storage move'
+ end
+ get ':repository_storage_move_id' do
+ storage_move = ProjectRepositoryStorageMove.find(params[:repository_storage_move_id])
+
+ present storage_move, with: Entities::ProjectRepositoryStorageMove, current_user: current_user
+ 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 'Get a list of all project repository storage moves' do
+ detail 'This feature was introduced in GitLab 13.1.'
+ success Entities::ProjectRepositoryStorageMove
+ end
+ params do
+ use :pagination
+ end
+ get ':id/repository_storage_moves' do
+ storage_moves = user_project.repository_storage_moves.with_projects.order_created_at_desc
+
+ present paginate(storage_moves), with: Entities::ProjectRepositoryStorageMove, current_user: current_user
+ end
+
+ desc 'Get a project repository storage move' do
+ detail 'This feature was introduced in GitLab 13.1.'
+ success Entities::ProjectRepositoryStorageMove
+ end
+ params do
+ requires :repository_storage_move_id, type: Integer, desc: 'The ID of a project repository storage move'
+ end
+ get ':id/repository_storage_moves/:repository_storage_move_id' do
+ storage_move = user_project.repository_storage_moves.find(params[:repository_storage_move_id])
present storage_move, with: Entities::ProjectRepositoryStorageMove, current_user: current_user
end
+
+ desc 'Schedule a project repository storage move' do
+ detail 'This feature was introduced in GitLab 13.1.'
+ success Entities::ProjectRepositoryStorageMove
+ end
+ params do
+ requires :destination_storage_name, type: String, desc: 'The destination storage shard'
+ end
+ post ':id/repository_storage_moves' do
+ storage_move = user_project.repository_storage_moves.build(
+ declared_params.merge(source_storage_name: user_project.repository_storage)
+ )
+
+ if storage_move.schedule
+ present storage_move, with: Entities::ProjectRepositoryStorageMove, current_user: current_user
+ else
+ render_validation_error!(storage_move)
+ end
+ end
end
end
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index f305da681c4..e00fb61f478 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -502,7 +502,9 @@ module API
link = user_project.project_group_links.find_by(group_id: params[:group_id])
not_found!('Group Link') unless link
- destroy_conditionally!(link)
+ destroy_conditionally!(link) do
+ ::Projects::GroupLinks::DestroyService.new(user_project, current_user).execute(link)
+ end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index f72230c084c..07c27f39539 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -40,6 +40,7 @@ module API
requires :name, type: String, desc: 'The name of the link'
requires :url, type: String, desc: 'The URL of the link'
optional :filepath, type: String, desc: 'The filepath of the link'
+ optional :link_type, type: String, desc: 'The link type'
end
post 'links' do
authorize! :create_release, release
@@ -75,6 +76,7 @@ module API
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: 'The filepath of the link'
+ optional :link_type, type: String, desc: 'The link type'
at_least_one_of :name, :url
end
put do
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index 95b3e90323c..a5bb1a44f1f 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -67,7 +67,6 @@ module API
if result[:status] == :success
log_release_created_audit_event(result[:release])
- create_evidence!
present result[:release], with: Entities::Release, current_user: current_user
else
@@ -169,16 +168,6 @@ module API
def log_release_milestones_updated_audit_event
# This is a separate method so that EE can extend its behaviour
end
-
- def create_evidence!
- return if release.historical_release?
-
- if release.upcoming_release?
- CreateEvidenceWorker.perform_at(release.released_at, release.id) # rubocop:disable CodeReuse/Worker
- else
- CreateEvidenceWorker.perform_async(release.id) # rubocop:disable CodeReuse/Worker
- end
- end
end
end
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 0b2df85f61f..bf4f08ce390 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -6,6 +6,8 @@ module API
class Repositories < Grape::API
include PaginationParams
+ helpers ::API::Helpers::HeadersHelpers
+
before { authorize! :download_code, user_project }
params do
@@ -67,6 +69,8 @@ module API
get ':id/repository/blobs/:sha/raw' do
assign_blob_vars!
+ no_cache_headers
+
send_git_blob @repo, @blob
end
diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb
index f7f7c881f4a..1fa6898b92c 100644
--- a/lib/api/resource_label_events.rb
+++ b/lib/api/resource_label_events.rb
@@ -27,10 +27,9 @@ module API
get ":id/#{eventables_str}/:eventable_id/resource_label_events" do
eventable = find_noteable(eventable_type, params[:eventable_id])
- opts = { page: params[:page], per_page: params[:per_page] }
- events = ResourceLabelEventFinder.new(current_user, eventable, opts).execute
+ events = eventable.resource_label_events.inc_relations
- present paginate(events), with: Entities::ResourceLabelEvent
+ present ResourceLabelEvent.visible_to_user?(current_user, paginate(events)), with: Entities::ResourceLabelEvent
end
desc "Get a single #{eventable_type.to_s.downcase} resource label event" do
diff --git a/lib/api/resource_milestone_events.rb b/lib/api/resource_milestone_events.rb
new file mode 100644
index 00000000000..30ff5a9b4be
--- /dev/null
+++ b/lib/api/resource_milestone_events.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module API
+ class ResourceMilestoneEvents < Grape::API
+ include PaginationParams
+ helpers ::API::Helpers::NotesHelpers
+
+ before { authenticate! }
+
+ [Issue, MergeRequest].each do |eventable_type|
+ parent_type = eventable_type.parent_class.to_s.underscore
+ eventables_str = eventable_type.to_s.underscore.pluralize
+
+ params do
+ requires :id, type: String, desc: "The ID of a #{parent_type}"
+ end
+ resource parent_type.pluralize.to_sym, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc "Get a list of #{eventable_type.to_s.downcase} resource milestone events" do
+ success Entities::ResourceMilestoneEvent
+ end
+ params do
+ requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable'
+ use :pagination
+ end
+
+ get ":id/#{eventables_str}/:eventable_id/resource_milestone_events" do
+ eventable = find_noteable(eventable_type, params[:eventable_id])
+
+ opts = { page: params[:page], per_page: params[:per_page] }
+ events = ResourceMilestoneEventFinder.new(current_user, eventable, opts).execute
+
+ present paginate(events), with: Entities::ResourceMilestoneEvent
+ end
+
+ desc "Get a single #{eventable_type.to_s.downcase} resource milestone event" do
+ success Entities::ResourceMilestoneEvent
+ end
+ params do
+ requires :event_id, type: String, desc: 'The ID of a resource milestone event'
+ requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable'
+ end
+ get ":id/#{eventables_str}/:eventable_id/resource_milestone_events/:event_id" do
+ eventable = find_noteable(eventable_type, params[:eventable_id])
+
+ event = eventable.resource_milestone_events.find(params[:event_id])
+
+ not_found!('ResourceMilestoneEvent') unless can?(current_user, :read_milestone, event.milestone_parent)
+
+ present event, with: Entities::ResourceMilestoneEvent
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 9095aba7340..5f08ebe4a06 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -154,7 +154,6 @@ module API
end
put '/:id' do
job = authenticate_job!
- job_forbidden!(job, 'Job is not running') unless job.running?
job.trace.set(params[:trace]) if params[:trace]
@@ -182,7 +181,6 @@ module API
end
patch '/:id/trace' do
job = authenticate_job!
- job_forbidden!(job, 'Job is not running') unless job.running?
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
content_range = request.headers['Content-Range']
@@ -220,6 +218,8 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
optional :filesize, type: Integer, desc: %q(Artifacts filesize)
+ optional :artifact_type, type: String, desc: %q(The type of artifact),
+ default: 'archive', values: Ci::JobArtifact.file_types.keys
end
post '/:id/artifacts/authorize' do
not_allowed! unless Gitlab.config.artifacts.enabled
@@ -227,18 +227,15 @@ module API
Gitlab::Workhorse.verify_api_request!(headers)
job = authenticate_job!
- forbidden!('Job is not running') unless job.running?
- max_size = max_artifacts_size(job)
+ service = Ci::AuthorizeJobArtifactService.new(job, params, max_size: max_artifacts_size(job))
- if params[:filesize]
- file_size = params[:filesize].to_i
- file_too_large! unless file_size < max_size
- end
+ forbidden! if service.forbidden?
+ file_too_large! if service.too_large?
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
- JobArtifactUploader.workhorse_authorize(has_length: false, maximum_size: max_size)
+ service.headers
end
desc 'Upload artifacts for job' do
@@ -265,7 +262,6 @@ module API
require_gitlab_workhorse!
job = authenticate_job!
- forbidden!('Job is not running!') unless job.running?
artifacts = params[:file]
metadata = params[:metadata]
@@ -292,7 +288,7 @@ module API
optional :direct_download, default: false, type: Boolean, desc: %q(Perform direct download from remote storage instead of proxying artifacts)
end
get '/:id/artifacts' do
- job = authenticate_job!
+ job = authenticate_job!(require_running: false)
present_carrierwave_file!(job.artifacts_file, supports_direct_download: params[:direct_download])
end
diff --git a/lib/api/search.rb b/lib/api/search.rb
index 3d2d4527e30..ac00d3682a0 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -20,6 +20,13 @@ module API
users: Entities::UserBasic
}.freeze
+ SCOPE_PRELOAD_METHOD = {
+ merge_requests: :with_api_entity_associations,
+ projects: :with_api_entity_associations,
+ issues: :with_api_entity_associations,
+ milestones: :with_api_entity_associations
+ }.freeze
+
def search(additional_params = {})
search_params = {
scope: params[:scope],
@@ -29,7 +36,9 @@ module API
per_page: params[:per_page]
}.merge(additional_params)
- results = SearchService.new(current_user, search_params).search_objects
+ results = SearchService.new(current_user, search_params).search_objects(preload_method)
+
+ Gitlab::UsageDataCounters::SearchCounter.count(:all_searches)
paginate(results)
end
@@ -42,6 +51,10 @@ module API
SCOPE_ENTITY[params[:scope].to_sym]
end
+ def preload_method
+ SCOPE_PRELOAD_METHOD[params[:scope].to_sym]
+ end
+
def verify_search_scope!(resource:)
# In EE we have additional validation requirements for searches.
# Defining this method here as a noop allows us to easily extend it in
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index e3a8f0671ef..0bf5eed26b4 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -83,6 +83,7 @@ module API
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts"
optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
+ optional :max_import_size, type: Integer, desc: 'Maximum import size in MB'
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
optional :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.'
optional :password_authentication_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
@@ -113,6 +114,7 @@ module API
end
optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues."
optional :repository_storages, type: Array[String], desc: 'Storage paths for new projects'
+ optional :repository_storages_weighted, type: Hash, desc: 'Storage paths for new projects with a weighted value between 0 and 100'
optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to set up Two-factor authentication'
given require_two_factor_authentication: ->(val) { val } do
requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
@@ -132,6 +134,10 @@ module API
given sourcegraph_enabled: ->(val) { val } do
requires :sourcegraph_url, type: String, desc: 'The configured Sourcegraph instance URL'
end
+ optional :spam_check_endpoint_enabled, type: Boolean, desc: 'Enable Spam Check via external API endpoint'
+ given spam_check_endpoint_enabled: ->(val) { val } do
+ requires :spam_check_endpoint_url, type: String, desc: 'The URL of the external Spam Check service endpoint'
+ end
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :instance_statistics_visibility_private, type: Boolean, desc: 'When set to `true` Instance statistics will only be available to admins'
diff --git a/lib/api/suggestions.rb b/lib/api/suggestions.rb
index d008d1b9e97..05aaa8a6f41 100644
--- a/lib/api/suggestions.rb
+++ b/lib/api/suggestions.rb
@@ -14,18 +14,51 @@ module API
put ':id/apply' do
suggestion = Suggestion.find_by_id(params[:id])
- not_found! unless suggestion
- authorize! :apply_suggestion, suggestion
+ if suggestion
+ apply_suggestions(suggestion, current_user)
+ else
+ render_api_error!(_('Suggestion is not applicable as the suggestion was not found.'), :not_found)
+ end
+ end
+
+ desc 'Apply multiple suggestion patches in the Merge Request where they were created' do
+ success Entities::Suggestion
+ end
+ params do
+ requires :ids, type: Array[String], desc: "An array of suggestion ID's"
+ end
+ put 'batch_apply' do
+ ids = params[:ids]
+
+ suggestions = Suggestion.id_in(ids)
- result = ::Suggestions::ApplyService.new(current_user).execute(suggestion)
+ if suggestions.size == ids.length
+ apply_suggestions(suggestions, current_user)
+ else
+ render_api_error!(_('Suggestions are not applicable as one or more suggestions were not found.'), :not_found)
+ end
+ end
+ end
+
+ helpers do
+ def apply_suggestions(suggestions, current_user)
+ authorize_suggestions(*suggestions)
+
+ result = ::Suggestions::ApplyService.new(current_user, *suggestions).execute
if result[:status] == :success
- present suggestion, with: Entities::Suggestion, current_user: current_user
+ present suggestions, with: Entities::Suggestion, current_user: current_user
else
- http_status = result[:http_status] || 400
+ http_status = result[:http_status] || :bad_request
render_api_error!(result[:message], http_status)
end
end
+
+ def authorize_suggestions(*suggestions)
+ suggestions.each do |suggestion|
+ authorize! :apply_suggestion, suggestion
+ end
+ end
end
end
end
diff --git a/lib/api/terraform/state.rb b/lib/api/terraform/state.rb
index 5141d1fd499..e7c9627c753 100644
--- a/lib/api/terraform/state.rb
+++ b/lib/api/terraform/state.rb
@@ -32,7 +32,7 @@ module API
end
desc 'Get a terraform state by its name'
- route_setting :authentication, basic_auth_personal_access_token: true
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
get do
remote_state_handler.find_with_lock do |state|
no_content! unless state.file.exists?
@@ -44,7 +44,7 @@ module API
end
desc 'Add a new terraform state or update an existing one'
- route_setting :authentication, basic_auth_personal_access_token: true
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
post do
data = request.body.read
no_content! if data.empty?
@@ -57,7 +57,7 @@ module API
end
desc 'Delete a terraform state of a certain name'
- route_setting :authentication, basic_auth_personal_access_token: true
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
delete do
remote_state_handler.handle_with_lock do |state|
state.destroy!
@@ -66,7 +66,7 @@ module API
end
desc 'Lock a terraform state of a certain name'
- route_setting :authentication, basic_auth_personal_access_token: true
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
params do
requires :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
requires :Operation, type: String, desc: 'Terraform operation'
@@ -103,7 +103,7 @@ module API
end
desc 'Unlock a terraform state of a certain name'
- route_setting :authentication, basic_auth_personal_access_token: true
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
params do
optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
end
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 02b8bb55274..e36ddf21277 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -6,8 +6,6 @@ module API
before { authenticate! }
- helpers ::Gitlab::IssuableMetadata
-
ISSUABLE_TYPES = {
'merge_requests' => ->(iid) { find_merge_request_with_access(iid) },
'issues' => ->(iid) { find_project_issue(iid) }
@@ -65,7 +63,7 @@ module API
next unless collection
targets = collection.map(&:target)
- options[type] = { issuable_metadata: issuable_meta_data(targets, type, current_user) }
+ options[type] = { issuable_metadata: Gitlab::IssuableMetadata.new(current_user, targets).data }
end
end
end
@@ -91,16 +89,18 @@ module API
requires :id, type: Integer, desc: 'The ID of the todo being marked as done'
end
post ':id/mark_as_done' do
- TodoService.new.mark_todos_as_done_by_ids(params[:id], current_user)
todo = current_user.todos.find(params[:id])
+ TodoService.new.resolve_todo(todo, current_user, resolved_by_action: :api_done)
+
present todo, with: Entities::Todo, current_user: current_user
end
desc 'Mark all todos as done'
post '/mark_as_done' do
todos = find_todos
- TodoService.new.mark_todos_as_done(todos, current_user)
+
+ TodoService.new.resolve_todos(todos, current_user, resolved_by_action: :api_all_done)
no_content!
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index c986414c223..3d8ae09edf1 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -55,6 +55,7 @@ module API
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'
optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
+ optional :note, type: String, desc: 'Admin note for this user'
all_or_none_of :extern_uid, :provider
use :optional_params_ee
@@ -254,6 +255,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the user'
requires :key, type: String, desc: 'The new SSH key'
requires :title, type: String, desc: 'The title of the new SSH key'
+ optional :expires_at, type: DateTime, desc: 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
end
# rubocop: disable CodeReuse/ActiveRecord
post ":id/keys" do
@@ -262,9 +264,9 @@ module API
user = User.find_by(id: params.delete(:id))
not_found!('User') unless user
- key = user.keys.new(declared_params(include_missing: false))
+ key = ::Keys::CreateService.new(current_user, declared_params(include_missing: false).merge(user: user)).execute
- if key.save
+ if key.persisted?
present key, with: Entities::SSHKey
else
render_validation_error!(key)
@@ -283,7 +285,8 @@ module API
user = find_user(params[:user_id])
not_found!('User') unless user && can?(current_user, :read_user, user)
- present paginate(user.keys), with: Entities::SSHKey
+ keys = user.keys.preload_users
+ present paginate(keys), with: Entities::SSHKey
end
desc 'Delete an existing SSH key from a specified user. Available only for admins.' do
@@ -303,7 +306,10 @@ module API
key = user.keys.find_by(id: params[:key_id])
not_found!('Key') unless key
- destroy_conditionally!(key)
+ destroy_conditionally!(key) do |key|
+ destroy_service = ::Keys::DestroyService.new(current_user)
+ destroy_service.execute(key)
+ end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -695,7 +701,9 @@ module API
use :pagination
end
get "keys" do
- present paginate(current_user.keys), with: Entities::SSHKey
+ keys = current_user.keys.preload_users
+
+ present paginate(keys), with: Entities::SSHKey
end
desc 'Get a single key owned by currently authenticated user' do
@@ -719,6 +727,7 @@ module API
params do
requires :key, type: String, desc: 'The new SSH key'
requires :title, type: String, desc: 'The title of the new SSH key'
+ optional :expires_at, type: DateTime, desc: 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
end
post "keys" do
key = current_user.keys.new(declared_params)
diff --git a/lib/api/validations/validators/file_path.rb b/lib/api/validations/validators/file_path.rb
index 93a20e5bf7d..fee71373170 100644
--- a/lib/api/validations/validators/file_path.rb
+++ b/lib/api/validations/validators/file_path.rb
@@ -8,7 +8,7 @@ module API
path = params[attr_name]
Gitlab::Utils.check_path_traversal!(path)
- rescue StandardError
+ rescue ::Gitlab::Utils::PathTraversalAttackError
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
message: "should be a valid file path"
end
diff --git a/lib/api/validations/validators/untrusted_regexp.rb b/lib/api/validations/validators/untrusted_regexp.rb
new file mode 100644
index 00000000000..ec623684e67
--- /dev/null
+++ b/lib/api/validations/validators/untrusted_regexp.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module API
+ module Validations
+ module Validators
+ class UntrustedRegexp < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ value = params[attr_name]
+ return unless value
+
+ Gitlab::UntrustedRegexp.new(value)
+ rescue RegexpError => e
+ message = "is an invalid regexp: #{e.message}"
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index 884e3019a2d..c1bf3a64923 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -24,7 +24,7 @@ module API
params :common_wiki_page_params do
optional :format,
type: String,
- values: ProjectWiki::MARKUPS.values.map(&:to_s),
+ values: Wiki::MARKUPS.values.map(&:to_s),
default: 'markdown',
desc: 'Format of a wiki page. Available formats are markdown, rdoc, asciidoc and org'
end