summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/api.rb4
-rw-r--r--lib/api/boards.rb2
-rw-r--r--lib/api/branches.rb4
-rw-r--r--lib/api/ci/jobs.rb35
-rw-r--r--lib/api/commits.rb10
-rw-r--r--lib/api/concerns/packages/debian_distribution_endpoints.rb55
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb10
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb4
-rw-r--r--lib/api/debian_group_packages.rb2
-rw-r--r--lib/api/deploy_keys.rb5
-rw-r--r--lib/api/discussions.rb2
-rw-r--r--lib/api/entities/alert_management/alert.rb12
-rw-r--r--lib/api/entities/ci/job_request/service.rb1
-rw-r--r--lib/api/entities/ci/lint/result.rb1
-rw-r--r--lib/api/entities/ci/runner.rb4
-rw-r--r--lib/api/entities/ci/runner_details.rb8
-rw-r--r--lib/api/entities/deploy_key.rb3
-rw-r--r--lib/api/entities/group.rb3
-rw-r--r--lib/api/entities/project.rb1
-rw-r--r--lib/api/entities/project_statistics.rb4
-rw-r--r--lib/api/entities/projects/topic.rb17
-rw-r--r--lib/api/entities/todo.rb2
-rw-r--r--lib/api/error_tracking/collector.rb24
-rw-r--r--lib/api/features.rb9
-rw-r--r--lib/api/files.rb10
-rw-r--r--lib/api/generic_packages.rb9
-rw-r--r--lib/api/github/entities.rb4
-rw-r--r--lib/api/group_boards.rb2
-rw-r--r--lib/api/group_debian_distributions.rb8
-rw-r--r--lib/api/group_labels.rb2
-rw-r--r--lib/api/group_milestones.rb2
-rw-r--r--lib/api/helpers.rb15
-rw-r--r--lib/api/helpers/award_emoji.rb2
-rw-r--r--lib/api/helpers/discussions_helpers.rb2
-rw-r--r--lib/api/helpers/file_upload_helpers.rb2
-rw-r--r--lib/api/helpers/integrations_helpers.rb41
-rw-r--r--lib/api/helpers/notes_helpers.rb2
-rw-r--r--lib/api/helpers/project_snapshots_helpers.rb2
-rw-r--r--lib/api/helpers/projects_helpers.rb13
-rw-r--r--lib/api/helpers/resource_label_events_helpers.rb2
-rw-r--r--lib/api/integrations.rb2
-rw-r--r--lib/api/internal/base.rb2
-rw-r--r--lib/api/internal/lfs.rb2
-rw-r--r--lib/api/invitations.rb2
-rw-r--r--lib/api/issue_links.rb2
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/labels.rb2
-rw-r--r--lib/api/lint.rb9
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/api/merge_request_approvals.rb6
-rw-r--r--lib/api/namespaces.rb4
-rw-r--r--lib/api/package_files.rb5
-rw-r--r--lib/api/project_debian_distributions.rb8
-rw-r--r--lib/api/project_milestones.rb2
-rw-r--r--lib/api/projects.rb12
-rw-r--r--lib/api/protected_branches.rb2
-rw-r--r--lib/api/releases.rb4
-rw-r--r--lib/api/repositories.rb41
-rw-r--r--lib/api/resource_milestone_events.rb2
-rw-r--r--lib/api/resource_state_events.rb2
-rw-r--r--lib/api/snippets.rb2
-rw-r--r--lib/api/subscriptions.rb6
-rw-r--r--lib/api/tags.rb16
-rw-r--r--lib/api/terraform/modules/v1/packages.rb3
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/api/topics.rb79
-rw-r--r--lib/api/users.rb1
-rw-r--r--lib/api/v3/github.rb32
-rw-r--r--lib/api/wikis.rb2
69 files changed, 463 insertions, 133 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index a4d42c735cb..dcecaeae558 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -27,7 +27,8 @@ module API
Gitlab::GrapeLogging::Loggers::PerfLogger.new,
Gitlab::GrapeLogging::Loggers::CorrelationIdLogger.new,
Gitlab::GrapeLogging::Loggers::ContextLogger.new,
- Gitlab::GrapeLogging::Loggers::ContentLogger.new
+ Gitlab::GrapeLogging::Loggers::ContentLogger.new,
+ Gitlab::GrapeLogging::Loggers::UrgencyLogger.new
]
allow_access_with_scope :api
@@ -283,6 +284,7 @@ module API
mount ::API::Tags
mount ::API::Templates
mount ::API::Todos
+ mount ::API::Topics
mount ::API::Unleash
mount ::API::UsageData
mount ::API::UsageDataQueries
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index 9e829dd5e05..56633c07774 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -7,7 +7,7 @@ module API
prepend_mod_with('API::BoardsResponses') # rubocop: disable Cop/InjectEnterpriseEditionModule
- feature_category :boards
+ feature_category :team_planning
before { authenticate! }
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 0db5bb82296..462c4a3de4c 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -41,7 +41,7 @@ module API
optional :page_token, type: String, desc: 'Name of branch to start the paginaition from'
end
- get ':id/repository/branches' do
+ get ':id/repository/branches', urgency: :low do
ff_enabled = Feature.enabled?(:api_caching_rate_limit_branches, user_project, default_enabled: :yaml)
cache_action_if(ff_enabled, [user_project, :branches, current_user, declared_params], expires_in: 30.seconds) do
@@ -86,7 +86,7 @@ module API
head do
user_project.repository.branch_exists?(params[:branch]) ? no_content! : not_found!
end
- get do
+ get '/', urgency: :low do
branch = find_branch!(params[:branch])
present branch, with: Entities::Branch, current_user: current_user, project: user_project
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index eea1637c32a..30ce1454419 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -177,6 +177,39 @@ module API
present current_authenticated_job, with: Entities::Ci::Job
end
+
+ desc 'Get current agents' do
+ detail 'Retrieves a list of agents for the given job token'
+ end
+ route_setting :authentication, job_token_allowed: true
+ get '/allowed_agents', feature_category: :kubernetes_management do
+ validate_current_authenticated_job
+
+ status 200
+
+ pipeline = current_authenticated_job.pipeline
+ project = current_authenticated_job.project
+ agent_authorizations = Clusters::AgentAuthorizationsFinder.new(project).execute
+ project_groups = project.group&.self_and_ancestor_ids&.map { |id| { id: id } } || []
+ user_access_level = project.team.max_member_access(current_user.id)
+ roles_in_project = Gitlab::Access.sym_options_with_owner
+ .select { |_role, role_access_level| role_access_level <= user_access_level }
+ .map(&:first)
+
+ environment = if environment_slug = current_authenticated_job.deployment&.environment&.slug
+ { slug: environment_slug }
+ end
+
+ # See https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/kubernetes_ci_access.md#apiv4joballowed_agents-api
+ {
+ allowed_agents: Entities::Clusters::AgentAuthorization.represent(agent_authorizations),
+ job: { id: current_authenticated_job.id },
+ pipeline: { id: pipeline.id },
+ project: { id: project.id, groups: project_groups },
+ user: { id: current_user.id, username: current_user.username, roles_in_project: roles_in_project },
+ environment: environment
+ }.compact
+ end
end
helpers do
@@ -202,5 +235,3 @@ module API
end
end
end
-
-API::Ci::Jobs.prepend_mod_with('API::Ci::Jobs')
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 10dc51556b9..8b8d8192524 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -27,7 +27,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS, urgency: :low do
desc 'Get a project repository commits' do
success Entities::Commit
end
@@ -43,7 +43,7 @@ module API
optional :trailers, type: Boolean, desc: 'Parse and include Git trailers for every commit', default: false
use :pagination
end
- get ':id/repository/commits' do
+ get ':id/repository/commits', urgency: :low do
path = params[:path]
before = params[:until]
after = params[:since]
@@ -169,7 +169,7 @@ module API
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
use :pagination
end
- get ':id/repository/commits/:sha/diff', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
+ get ':id/repository/commits/:sha/diff', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS, urgency: :low do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
@@ -295,7 +295,7 @@ module API
optional :type, type: String, values: %w[branch tag all], default: 'all', desc: 'Scope'
use :pagination
end
- get ':id/repository/commits/:sha/refs', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
+ get ':id/repository/commits/:sha/refs', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS, urgency: :low do
commit = user_project.commit(params[:sha])
not_found!('Commit') unless commit
@@ -363,7 +363,7 @@ module API
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag on which to find Merge Requests'
use :pagination
end
- get ':id/repository/commits/:sha/merge_requests', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
+ get ':id/repository/commits/:sha/merge_requests', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS, urgency: :low do
authorize! :read_merge_request, user_project
commit = user_project.commit(params[:sha])
diff --git a/lib/api/concerns/packages/debian_distribution_endpoints.rb b/lib/api/concerns/packages/debian_distribution_endpoints.rb
index 798e583b87a..ddc83d0f747 100644
--- a/lib/api/concerns/packages/debian_distribution_endpoints.rb
+++ b/lib/api/concerns/packages/debian_distribution_endpoints.rb
@@ -15,6 +15,12 @@ module API
helpers ::API::Helpers::Packages::BasicAuthHelpers
include ::API::Helpers::Authentication
+ helpers do
+ def distribution
+ ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last || not_found!('Distribution')
+ end
+ end
+
namespace 'debian_distributions' do
helpers do
params :optional_distribution_params do
@@ -36,9 +42,18 @@ module API
end
end
+ rescue_from ArgumentError do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ rescue_from ActiveRecord::RecordInvalid do |e|
+ render_api_error!(e.message, 400)
+ end
+
authenticate_with do |accept|
- accept.token_types(:personal_access_token, :deploy_token, :job_token)
- .sent_through(:http_basic_auth)
+ accept.token_types(:personal_access_token).sent_through(:http_private_token_header)
+ accept.token_types(:deploy_token).sent_through(:http_deploy_token_header)
+ accept.token_types(:job_token).sent_through(:http_job_token_header)
end
content_type :json, 'application/json'
@@ -59,12 +74,12 @@ module API
distribution_params = declared_params(include_missing: false)
result = ::Packages::Debian::CreateDistributionService.new(project_or_group, current_user, distribution_params).execute
- distribution = result.payload[:distribution]
+ created_distribution = result.payload[:distribution]
if result.success?
- present distribution, with: ::API::Entities::Packages::Debian::Distribution
+ present created_distribution, with: ::API::Entities::Packages::Debian::Distribution
else
- render_validation_error!(distribution)
+ render_validation_error!(created_distribution)
end
end
@@ -100,11 +115,28 @@ module API
get '/:codename' do
authorize_read_package!(project_or_group)
- distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last!
-
present distribution, with: ::API::Entities::Packages::Debian::Distribution
end
+ # GET {projects|groups}/:id/debian_distributions/:codename/key
+ desc 'Get a Debian Distribution Key' do
+ detail 'This feature was introduced in 14.4'
+ success ::API::Entities::Packages::Debian::Distribution
+ end
+
+ params do
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ end
+ get '/:codename/key.asc' do
+ authorize_read_package!(project_or_group)
+
+ content_type 'text/plain'
+ env['api.format'] = :binary
+ header 'Content-Disposition', "attachment; filename*=UTF-8''#{CGI.escape(params[:codename])}.asc"
+
+ distribution.key&.public_key || not_found!('Distribution key')
+ end
+
# PUT {projects|groups}/:id/debian_distributions/:codename
desc 'Update a Debian Distribution' do
detail 'This feature was introduced in 14.0'
@@ -118,15 +150,14 @@ module API
put '/:codename' do
authorize_create_package!(project_or_group)
- distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last!
distribution_params = declared_params(include_missing: false).except(:codename)
result = ::Packages::Debian::UpdateDistributionService.new(distribution, distribution_params).execute
- distribution = result.payload[:distribution]
+ updated_distribution = result.payload[:distribution]
if result.success?
- present distribution, with: ::API::Entities::Packages::Debian::Distribution
+ present updated_distribution, with: ::API::Entities::Packages::Debian::Distribution
else
- render_validation_error!(distribution)
+ render_validation_error!(updated_distribution)
end
end
@@ -142,8 +173,6 @@ module API
delete '/:codename' do
authorize_destroy_package!(project_or_group)
- distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last!
-
accepted! if distribution.destroy
render_api_error!('Failed to delete distribution', 400)
diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index 0acc015f366..d083643f3d0 100644
--- a/lib/api/concerns/packages/debian_package_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -43,11 +43,6 @@ module API
end
end
- authenticate_with do |accept|
- accept.token_types(:personal_access_token, :deploy_token, :job_token)
- .sent_through(:http_basic_auth)
- end
-
rescue_from ArgumentError do |e|
render_api_error!(e.message, 400)
end
@@ -56,6 +51,11 @@ module API
render_api_error!(e.message, 400)
end
+ authenticate_with do |accept|
+ accept.token_types(:personal_access_token, :deploy_token, :job_token)
+ .sent_through(:http_basic_auth)
+ end
+
format :txt
content_type :txt, 'text/plain'
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index d6e006df976..7a657be5bf3 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -121,7 +121,9 @@ module API
not_found!('Packages') if packages.empty?
- present ::Packages::Npm::PackagePresenter.new(package_name, packages),
+ include_metadata = Feature.enabled?(:packages_npm_abbreviated_metadata, project, default_enabled: :yaml)
+
+ present ::Packages::Npm::PackagePresenter.new(package_name, packages, include_metadata: include_metadata),
with: ::API::Entities::NpmPackage
end
end
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 29f5047230a..1f640cc17d0 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -32,7 +32,7 @@ module API
namespace ':id/-/packages/debian' do
include ::API::Concerns::Packages::DebianPackageEndpoints
- # GET groups/:id/packages/debian/pool/:distribution/:project_id/:letter/:package_name/:package_version/:file_name
+ # GET groups/:id/-/packages/debian/pool/:distribution/:project_id/:letter/:package_name/:package_version/:file_name
params do
requires :project_id, type: Integer, desc: 'The Project Id'
use :shared_package_file_params
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 9f0f569b711..0ab9fe6644c 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -23,11 +23,14 @@ module API
desc 'Return all deploy keys'
params do
use :pagination
+ optional :public, type: Boolean, default: false, desc: "Only return deploy keys that are public"
end
get "deploy_keys" do
authenticated_as_admin!
- present paginate(DeployKey.all), with: Entities::DeployKey
+ deploy_keys = params[:public] ? DeployKey.are_public : DeployKey.all
+
+ present paginate(deploy_keys.including_projects_with_write_access), with: Entities::DeployKey, include_projects_with_write_access: true
end
params do
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 580d546b360..cf4b2348458 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -239,7 +239,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def readable_discussion_notes(noteable, discussion_ids)
notes = noteable.notes
- .where(discussion_id: discussion_ids)
+ .with_discussion_ids(discussion_ids)
.inc_relations_for_view
.includes(:noteable)
.fresh
diff --git a/lib/api/entities/alert_management/alert.rb b/lib/api/entities/alert_management/alert.rb
new file mode 100644
index 00000000000..664cd53293e
--- /dev/null
+++ b/lib/api/entities/alert_management/alert.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module AlertManagement
+ class Alert < Grape::Entity
+ expose :iid
+ expose :title
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/service.rb b/lib/api/entities/ci/job_request/service.rb
index f89b95c1d5c..0dae5d5a933 100644
--- a/lib/api/entities/ci/job_request/service.rb
+++ b/lib/api/entities/ci/job_request/service.rb
@@ -6,6 +6,7 @@ module API
module JobRequest
class Service < Entities::Ci::JobRequest::Image
expose :alias, :command
+ expose :variables
end
end
end
diff --git a/lib/api/entities/ci/lint/result.rb b/lib/api/entities/ci/lint/result.rb
index 0e4aa238ba2..39039868bba 100644
--- a/lib/api/entities/ci/lint/result.rb
+++ b/lib/api/entities/ci/lint/result.rb
@@ -9,6 +9,7 @@ module API
expose :errors
expose :warnings
expose :merged_yaml
+ expose :jobs, if: -> (result, options) { options[:include_jobs] }
end
end
end
diff --git a/lib/api/entities/ci/runner.rb b/lib/api/entities/ci/runner.rb
index ede698696de..60193fe1df4 100644
--- a/lib/api/entities/ci/runner.rb
+++ b/lib/api/entities/ci/runner.rb
@@ -12,7 +12,9 @@ module API
expose :runner_type
expose :name
expose :online?, as: :online
- expose :status
+ # DEPRECATED
+ # TODO Remove in %15.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
+ expose :status, as: :deprecated_rest_status
end
end
end
diff --git a/lib/api/entities/ci/runner_details.rb b/lib/api/entities/ci/runner_details.rb
index 9d44da7e5b3..6ded1296f2a 100644
--- a/lib/api/entities/ci/runner_details.rb
+++ b/lib/api/entities/ci/runner_details.rb
@@ -15,18 +15,18 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
expose :projects, with: Entities::BasicProjectDetails do |runner, options|
if options[:current_user].admin? # rubocop: disable Cop/UserAdmin
- runner.projects
+ runner.projects.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338659')
else
- options[:current_user].authorized_projects.where(id: runner.projects)
+ options[:current_user].authorized_projects.where(id: runner.projects).allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338659')
end
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
expose :groups, with: Entities::BasicGroupDetails do |runner, options|
if options[:current_user].admin? # rubocop: disable Cop/UserAdmin
- runner.groups
+ runner.groups.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338659')
else
- options[:current_user].authorized_groups.where(id: runner.groups)
+ options[:current_user].authorized_groups.where(id: runner.groups).allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338659')
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/entities/deploy_key.rb b/lib/api/entities/deploy_key.rb
index ed922c24eda..e8537c4c677 100644
--- a/lib/api/entities/deploy_key.rb
+++ b/lib/api/entities/deploy_key.rb
@@ -4,6 +4,9 @@ module API
module Entities
class DeployKey < Entities::SSHKey
expose :key
+ expose :fingerprint
+
+ expose :projects_with_write_access, using: Entities::ProjectIdentity, if: -> (_, options) { options[:include_projects_with_write_access] }
end
end
end
diff --git a/lib/api/entities/group.rb b/lib/api/entities/group.rb
index 048b7a3c15a..246fb819890 100644
--- a/lib/api/entities/group.rb
+++ b/lib/api/entities/group.rb
@@ -31,7 +31,10 @@ module API
expose :wiki_size
expose :lfs_objects_size
expose :build_artifacts_size, as: :job_artifacts_size
+ expose :pipeline_artifacts_size
+ expose :packages_size
expose :snippets_size
+ expose :uploads_size
end
end
end
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 41320d184f9..e3f1e90b80f 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -114,6 +114,7 @@ module API
expose :merge_method
expose :squash_option
expose :suggestion_commit_message
+ expose :merge_commit_template
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
}
diff --git a/lib/api/entities/project_statistics.rb b/lib/api/entities/project_statistics.rb
index 70980e670b0..6544e8bc8ff 100644
--- a/lib/api/entities/project_statistics.rb
+++ b/lib/api/entities/project_statistics.rb
@@ -9,8 +9,10 @@ module API
expose :wiki_size
expose :lfs_objects_size
expose :build_artifacts_size, as: :job_artifacts_size
- expose :snippets_size
+ expose :pipeline_artifacts_size
expose :packages_size
+ expose :snippets_size
+ expose :uploads_size
end
end
end
diff --git a/lib/api/entities/projects/topic.rb b/lib/api/entities/projects/topic.rb
new file mode 100644
index 00000000000..d3d1cbec81c
--- /dev/null
+++ b/lib/api/entities/projects/topic.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Projects
+ class Topic < Grape::Entity
+ expose :id
+ expose :name
+ expose :description
+ expose :total_projects_count
+ expose :avatar_url do |topic, options|
+ topic.avatar_url(only_path: false)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/todo.rb b/lib/api/entities/todo.rb
index 8d222db488a..5bbbb59f565 100644
--- a/lib/api/entities/todo.rb
+++ b/lib/api/entities/todo.rb
@@ -33,7 +33,7 @@ module API
def todo_target_url(todo)
return design_todo_target_url(todo) if todo.for_design?
- target_type = todo.target_type.underscore
+ target_type = todo.target_type.gsub('::', '_').underscore
target_url = "#{todo.resource_parent.class.to_s.underscore}_#{target_type}_url"
Gitlab::Routing
diff --git a/lib/api/error_tracking/collector.rb b/lib/api/error_tracking/collector.rb
index 22fbd3a1118..13fda356257 100644
--- a/lib/api/error_tracking/collector.rb
+++ b/lib/api/error_tracking/collector.rb
@@ -12,6 +12,10 @@ module API
content_type :txt, 'text/plain'
default_format :envelope
+ rescue_from ActiveRecord::RecordInvalid do |e|
+ render_api_error!(e.message, 400)
+ end
+
before do
not_found!('Project') unless project
not_found! unless feature_enabled?
@@ -50,6 +54,12 @@ module API
bad_request!('Failed to parse sentry request')
end
end
+
+ def validate_payload(payload)
+ unless ::ErrorTracking::Collector::PayloadValidator.new.valid?(payload)
+ bad_request!('Unsupported sentry payload')
+ end
+ end
end
desc 'Submit error tracking event to the project as envelope' do
@@ -88,6 +98,8 @@ module API
# We don't have use for transaction request yet,
# so we record only event one.
if type == 'event'
+ validate_payload(parsed_request[:event])
+
::ErrorTracking::CollectErrorService
.new(project, nil, event: parsed_request[:event])
.execute
@@ -96,7 +108,10 @@ module API
# Collector should never return any information back.
# Because DSN and public key are designed for public use,
# it is safe only for submission of new events.
- no_content!
+ #
+ # Some clients sdk require status 200 OK to work correctly.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/343531.
+ status 200
end
desc 'Submit error tracking event to the project' do
@@ -122,6 +137,8 @@ module API
bad_request!('Failed to parse sentry request')
end
+ validate_payload(parsed_body)
+
::ErrorTracking::CollectErrorService
.new(project, nil, event: parsed_body)
.execute
@@ -129,7 +146,10 @@ module API
# Collector should never return any information back.
# Because DSN and public key are designed for public use,
# it is safe only for submission of new events.
- no_content!
+ #
+ # Some clients sdk require status 200 OK to work correctly.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/343531.
+ status 200
end
end
end
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 2ce2f7c518f..398e57794c8 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -14,7 +14,12 @@ module API
when '0', 'false'
false
else
- params[:value].to_i
+ # https://github.com/jnunemaker/flipper/blob/master/lib/flipper/typecast.rb#L47
+ if params[:value].to_s.include?('.')
+ params[:value].to_f
+ else
+ params[:value].to_i
+ end
end
end
@@ -59,7 +64,7 @@ module API
success Entities::Feature
end
params do
- requires :value, type: String, desc: '`true` or `false` to enable/disable, an integer for percentage of time'
+ requires :value, type: String, desc: '`true` or `false` to enable/disable, a float for percentage of time'
optional :key, type: String, desc: '`percentage_of_actors` or the default `percentage_of_time`'
optional :feature_group, type: String, desc: 'A Feature group name'
optional :user, type: String, desc: 'A GitLab username'
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 9d2b7cce837..39b3904ec90 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -122,7 +122,7 @@ module API
requires :file_path, type: String, file_path: true, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
optional :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false
end
- head ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
+ head ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
assign_file_vars!
set_http_headers(blob_data)
@@ -133,7 +133,7 @@ module API
requires :file_path, type: String, file_path: true, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
optional :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false
end
- get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
+ get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
assign_file_vars!
no_cache_headers
@@ -147,7 +147,7 @@ module API
requires :file_path, type: String, file_path: true, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false
end
- head ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
+ head ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
assign_file_vars!
set_http_headers(blob_data)
@@ -174,7 +174,7 @@ module API
params do
use :extended_file_params
end
- post ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
+ post ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
@@ -192,7 +192,7 @@ module API
params do
use :extended_file_params
end
- put ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
+ put ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 5e184d35255..8cca3378eec 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -54,6 +54,7 @@ module API
requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.generic_package_file_name_regex, file_path: true
optional :status, type: String, values: ALLOWED_STATUSES, desc: 'Package status'
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ optional :select, type: String, values: %w[package_file]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
@@ -65,11 +66,15 @@ module API
track_package_event('push_package', :generic, project: project, user: current_user, namespace: project.namespace)
create_package_file_params = declared_params.merge(build: current_authenticated_job)
- ::Packages::Generic::CreatePackageFileService
+ package_file = ::Packages::Generic::CreatePackageFileService
.new(project, current_user, create_package_file_params)
.execute
- created!
+ if params[:select] == 'package_file'
+ present package_file
+ else
+ created!
+ end
rescue ObjectStorage::RemoteStoreError => e
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project.id })
diff --git a/lib/api/github/entities.rb b/lib/api/github/entities.rb
index fe228c9a2d2..125985f0e23 100644
--- a/lib/api/github/entities.rb
+++ b/lib/api/github/entities.rb
@@ -59,8 +59,8 @@ module API
expose :parents do |commit|
commit.parent_ids.map { |id| { sha: id } }
end
- expose :files do |commit|
- commit.diffs.diff_files.flat_map do |diff|
+ expose :files do |_commit, options|
+ options[:diff_files].flat_map do |diff|
additions = diff.added_lines
deletions = diff.removed_lines
diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb
index 92869f8fbba..e9350da555c 100644
--- a/lib/api/group_boards.rb
+++ b/lib/api/group_boards.rb
@@ -7,7 +7,7 @@ module API
prepend_mod_with('API::BoardsResponses') # rubocop: disable Cop/InjectEnterpriseEditionModule
- feature_category :boards
+ feature_category :team_planning
before { authenticate! }
diff --git a/lib/api/group_debian_distributions.rb b/lib/api/group_debian_distributions.rb
index 01a8774bd97..f0376fe2c9c 100644
--- a/lib/api/group_debian_distributions.rb
+++ b/lib/api/group_debian_distributions.rb
@@ -7,14 +7,6 @@ module API
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- rescue_from ArgumentError do |e|
- render_api_error!(e.message, 400)
- end
-
- rescue_from ActiveRecord::RecordInvalid do |e|
- render_api_error!(e.message, 400)
- end
-
after_validation do
require_packages_enabled!
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index bea538441ee..7c1f23be828 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -7,7 +7,7 @@ module API
before { authenticate! }
- feature_category :issue_tracking
+ feature_category :team_planning
params do
requires :id, type: String, desc: 'The ID of a group'
diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb
index 061d0410a9c..b097022e9c1 100644
--- a/lib/api/group_milestones.rb
+++ b/lib/api/group_milestones.rb
@@ -7,7 +7,7 @@ module API
before { authenticate! }
- feature_category :issue_tracking
+ feature_category :team_planning
params do
requires :id, type: String, desc: 'The ID of a group'
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index f9ba5ba8186..76840091112 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -174,9 +174,9 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def find_namespace(id)
if id.to_s =~ /^\d+$/
- Namespace.find_by(id: id)
+ Namespace.without_project_namespaces.find_by(id: id)
else
- Namespace.find_by_full_path(id)
+ find_namespace_by_path(id)
end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -186,7 +186,7 @@ module API
end
def find_namespace_by_path(path)
- Namespace.find_by_full_path(path)
+ Namespace.without_project_namespaces.find_by_full_path(path)
end
def find_namespace_by_path!(path)
@@ -488,7 +488,7 @@ module API
def handle_api_exception(exception)
if report_exception?(exception)
define_params_for_grape_middleware
- Gitlab::ApplicationContext.push(user: current_user)
+ Gitlab::ApplicationContext.push(user: current_user, remote_ip: request.ip)
Gitlab::ErrorTracking.track_exception(exception)
end
@@ -681,20 +681,27 @@ module API
def send_git_blob(repository, blob)
env['api.format'] = :txt
content_type 'text/plain'
+
header['Content-Disposition'] = ActionDispatch::Http::ContentDisposition.format(disposition: 'inline', filename: blob.name)
# Let Workhorse examine the content and determine the better content disposition
header[Gitlab::Workhorse::DETECT_HEADER] = "true"
header(*Gitlab::Workhorse.send_git_blob(repository, blob))
+
+ body ''
end
def send_git_archive(repository, **kwargs)
header(*Gitlab::Workhorse.send_git_archive(repository, **kwargs))
+
+ body ''
end
def send_artifacts_entry(file, entry)
header(*Gitlab::Workhorse.send_artifacts_entry(file, entry))
+
+ body ''
end
# The Grape Error Middleware only has access to `env` but not `params` nor
diff --git a/lib/api/helpers/award_emoji.rb b/lib/api/helpers/award_emoji.rb
index 5b659c4dde7..3ea35381c97 100644
--- a/lib/api/helpers/award_emoji.rb
+++ b/lib/api/helpers/award_emoji.rb
@@ -5,7 +5,7 @@ module API
module AwardEmoji
def self.awardables
[
- { type: 'issue', resource: :projects, find_by: :iid, feature_category: :issue_tracking },
+ { type: 'issue', resource: :projects, find_by: :iid, feature_category: :team_planning },
{ type: 'merge_request', resource: :projects, find_by: :iid, feature_category: :code_review },
{ type: 'snippet', resource: :projects, find_by: :id, feature_category: :snippets }
]
diff --git a/lib/api/helpers/discussions_helpers.rb b/lib/api/helpers/discussions_helpers.rb
index cb2feeda1e1..c94199b17bc 100644
--- a/lib/api/helpers/discussions_helpers.rb
+++ b/lib/api/helpers/discussions_helpers.rb
@@ -7,7 +7,7 @@ module API
# This is a method instead of a constant, allowing EE to more easily
# extend it.
{
- Issue => :issue_tracking,
+ Issue => :team_planning,
Snippet => :snippets,
MergeRequest => :code_review,
Commit => :code_review
diff --git a/lib/api/helpers/file_upload_helpers.rb b/lib/api/helpers/file_upload_helpers.rb
index dd551ec2976..751972b44f0 100644
--- a/lib/api/helpers/file_upload_helpers.rb
+++ b/lib/api/helpers/file_upload_helpers.rb
@@ -5,7 +5,7 @@ module API
module FileUploadHelpers
def file_is_valid?
filename = params[:file]&.original_filename
- filename && ImportExportUploader::EXTENSION_WHITELIST.include?(File.extname(filename).delete('.'))
+ filename && ImportExportUploader::EXTENSION_ALLOWLIST.include?(File.extname(filename).delete('.'))
end
def validate_file!
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index e0ef9099104..e7fdb6645a5 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -254,7 +254,7 @@ module API
type: Boolean,
desc: 'DEPRECATED: This parameter has no effect since SSL verification will always be enabled'
}
- ],
+ ],
'campfire' => [
{
required: true,
@@ -530,6 +530,14 @@ module API
desc: 'The Mattermost token'
}
],
+ 'shimo' => [
+ {
+ required: true,
+ name: :external_wiki_url,
+ type: String,
+ desc: 'Shimo workspace URL'
+ }
+ ],
'slack-slash-commands' => [
{
required: true,
@@ -768,7 +776,33 @@ module API
desc: 'The Webex Teams webhook. For example, https://api.ciscospark.com/v1/webhooks/incoming/...'
},
chat_notification_events
- ].flatten
+ ].flatten,
+ 'zentao' => [
+ {
+ required: true,
+ name: :url,
+ type: String,
+ desc: 'The base URL to the ZenTao instance web interface which is being linked to this GitLab project. For example, https://www.zentao.net'
+ },
+ {
+ required: false,
+ name: :api_url,
+ type: String,
+ desc: 'The base URL to the ZenTao instance API. Web URL value will be used if not set. For example, https://www.zentao.net'
+ },
+ {
+ required: true,
+ name: :api_token,
+ type: String,
+ desc: 'The API token created from ZenTao dashboard'
+ },
+ {
+ required: true,
+ name: :zentao_product_xid,
+ type: String,
+ desc: 'The product ID of ZenTao project'
+ }
+ ]
}
end
@@ -805,7 +839,8 @@ module API
::Integrations::Slack,
::Integrations::SlackSlashCommands,
::Integrations::Teamcity,
- ::Integrations::Youtrack
+ ::Integrations::Youtrack,
+ ::Integrations::Zentao
]
end
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index 356e4a98c97..45671b09be9 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -7,7 +7,7 @@ module API
def self.feature_category_per_noteable_type
{
- Issue => :issue_tracking,
+ Issue => :team_planning,
MergeRequest => :code_review,
Snippet => :snippets
}
diff --git a/lib/api/helpers/project_snapshots_helpers.rb b/lib/api/helpers/project_snapshots_helpers.rb
index 0b10641571a..4b48661eeca 100644
--- a/lib/api/helpers/project_snapshots_helpers.rb
+++ b/lib/api/helpers/project_snapshots_helpers.rb
@@ -11,6 +11,8 @@ module API
def send_git_snapshot(repository)
header(*Gitlab::Workhorse.send_git_snapshot(repository))
+
+ body ''
end
def snapshot_project
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 30edbe91125..42d1c40dd11 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -61,6 +61,7 @@ module API
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions'
+ optional :merge_commit_template, type: String, desc: 'Template used to create merge commit message'
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled'
@@ -160,6 +161,7 @@ module API
:wiki_access_level,
:avatar,
:suggestion_commit_message,
+ :merge_commit_template,
:repository_storage,
:compliance_framework_setting,
:packages_enabled,
@@ -178,6 +180,17 @@ module API
def filter_attributes_using_license!(attrs)
end
+
+ def validate_git_import_url!(import_url, import_enabled: true)
+ return if import_url.blank?
+ return unless import_enabled
+
+ result = Import::ValidateRemoteGitEndpointService.new(url: import_url).execute # network call
+
+ if result.error?
+ render_api_error!(result.message, 422)
+ end
+ end
end
end
end
diff --git a/lib/api/helpers/resource_label_events_helpers.rb b/lib/api/helpers/resource_label_events_helpers.rb
index 7e641130062..eeb68362c1d 100644
--- a/lib/api/helpers/resource_label_events_helpers.rb
+++ b/lib/api/helpers/resource_label_events_helpers.rb
@@ -7,7 +7,7 @@ module API
# This is a method instead of a constant, allowing EE to more easily
# extend it.
{
- Issue => :issue_tracking,
+ Issue => :team_planning,
MergeRequest => :code_review
}
end
diff --git a/lib/api/integrations.rb b/lib/api/integrations.rb
index 926cde340a0..bab8e556a73 100644
--- a/lib/api/integrations.rb
+++ b/lib/api/integrations.rb
@@ -153,7 +153,7 @@ module API
requires setting[:name], type: setting[:type], desc: setting[:desc]
end
end
- post "#{path}/#{integration_slug.underscore}/trigger" do
+ post "#{path}/#{integration_slug.underscore}/trigger", urgency: :low do
project = find_project(params[:id])
# This is not accurate, but done to prevent leakage of the project names
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index dc9257ebd62..d8e39d089e4 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -145,7 +145,7 @@ module API
check_allowed(params)
end
- post "/lfs_authenticate", feature_category: :source_code_management do
+ post "/lfs_authenticate", feature_category: :source_code_management, urgency: :high do
not_found! unless container&.lfs_enabled?
status 200
diff --git a/lib/api/internal/lfs.rb b/lib/api/internal/lfs.rb
index 66baa4f1034..e94da8d34e0 100644
--- a/lib/api/internal/lfs.rb
+++ b/lib/api/internal/lfs.rb
@@ -24,7 +24,7 @@ module API
requires :oid, type: String, desc: 'The object ID to query'
requires :gl_repository, type: String, desc: "Project identifier (e.g. project-1)"
end
- get "/" do
+ get "/", urgency: :high do
lfs_object = find_lfs_object(params[:oid])
not_found! unless lfs_object
diff --git a/lib/api/invitations.rb b/lib/api/invitations.rb
index 5cade301d81..f7f5af07378 100644
--- a/lib/api/invitations.rb
+++ b/lib/api/invitations.rb
@@ -25,6 +25,8 @@ module API
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'invitations-api'
optional :areas_of_focus, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Areas the inviter wants the member to focus upon'
+ optional :tasks_to_be_done, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Tasks the inviter wants the member to do'
+ optional :tasks_project_id, type: Integer, desc: 'The project ID in which to create the task issues'
end
post ":id/invitations" do
params[:source] = find_source(source_type, params[:id])
diff --git a/lib/api/issue_links.rb b/lib/api/issue_links.rb
index 0b4f4e06d0b..98451afb12d 100644
--- a/lib/api/issue_links.rb
+++ b/lib/api/issue_links.rb
@@ -6,7 +6,7 @@ module API
before { authenticate! }
- feature_category :issue_tracking
+ feature_category :team_planning
params do
requires :id, type: String, desc: 'The ID of a project'
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 43e83bd58fe..9958526fa7f 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -7,7 +7,7 @@ module API
before { authenticate_non_get! }
- feature_category :issue_tracking
+ feature_category :team_planning
helpers do
params :negatable_issue_filter_params do
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index aa3746dae42..e3253d15c15 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -7,7 +7,7 @@ module API
before { authenticate! }
- feature_category :issue_tracking
+ feature_category :team_planning
LABEL_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
name: API::NO_SLASH_URL_PART_REGEX,
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index fa871b4bc57..f1e19e9c3c5 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -9,6 +9,7 @@ module API
params do
requires :content, type: String, desc: 'Content of .gitlab-ci.yml'
optional :include_merged_yaml, type: Boolean, desc: 'Whether or not to include merged CI config yaml in the response'
+ optional :include_jobs, type: Boolean, desc: 'Whether or not to include CI jobs in the response'
end
post '/lint' do
unauthorized! if (Gitlab::CurrentSettings.signup_disabled? || Gitlab::CurrentSettings.signup_limited?) && current_user.nil?
@@ -17,7 +18,7 @@ module API
.validate(params[:content], dry_run: false)
status 200
- Entities::Ci::Lint::Result.represent(result, current_user: current_user).serializable_hash.tap do |presented_result|
+ Entities::Ci::Lint::Result.represent(result, current_user: current_user, include_jobs: params[:include_jobs]).serializable_hash.tap do |presented_result|
presented_result[:status] = presented_result[:valid] ? 'valid' : 'invalid'
presented_result.delete(:merged_yaml) unless params[:include_merged_yaml]
end
@@ -30,6 +31,7 @@ module API
end
params do
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
+ optional :include_jobs, type: Boolean, desc: 'Whether or not to include CI jobs in the response'
end
get ':id/ci/lint' do
authorize! :download_code, user_project
@@ -39,7 +41,7 @@ module API
.new(project: user_project, current_user: current_user)
.validate(content, dry_run: params[:dry_run])
- present result, with: Entities::Ci::Lint::Result, current_user: current_user
+ present result, with: Entities::Ci::Lint::Result, current_user: current_user, include_jobs: params[:include_jobs]
end
end
@@ -50,6 +52,7 @@ module API
params do
requires :content, type: String, desc: 'Content of .gitlab-ci.yml'
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
+ optional :include_jobs, type: Boolean, desc: 'Whether or not to include CI jobs in the response'
end
post ':id/ci/lint' do
authorize! :create_pipeline, user_project
@@ -59,7 +62,7 @@ module API
.validate(params[:content], dry_run: params[:dry_run])
status 200
- present result, with: Entities::Ci::Lint::Result, current_user: current_user
+ present result, with: Entities::Ci::Lint::Result, current_user: current_user, include_jobs: params[:include_jobs]
end
end
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 332520ccd26..f488c8c26fc 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -95,6 +95,8 @@ module API
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'members-api'
optional :areas_of_focus, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Areas the inviter wants the member to focus upon'
+ optional :tasks_to_be_done, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Tasks the inviter wants the member to do'
+ optional :tasks_project_id, type: Integer, desc: 'The project ID in which to create the task issues'
end
post ":id/members" do
diff --git a/lib/api/merge_request_approvals.rb b/lib/api/merge_request_approvals.rb
index 83150bb51ca..dd49624c74f 100644
--- a/lib/api/merge_request_approvals.rb
+++ b/lib/api/merge_request_approvals.rb
@@ -25,7 +25,7 @@ module API
# Examples:
# GET /projects/:id/merge_requests/:merge_request_iid/approvals
desc 'List approvals for merge request'
- get 'approvals' do
+ get 'approvals', urgency: :low do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -47,7 +47,7 @@ module API
use :ee_approval_params
end
- post 'approve' do
+ post 'approve', urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid], :approve_merge_request)
check_sha_param!(params, merge_request)
@@ -63,7 +63,7 @@ module API
end
desc 'Remove an approval from a merge request'
- post 'unapprove' do
+ post 'unapprove', urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid], :approve_merge_request)
success = ::MergeRequests::RemoveApprovalService
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index c2d839571a6..d2468fb1c2e 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -37,7 +37,7 @@ module API
namespaces = current_user.admin ? Namespace.all : current_user.namespaces(owned_only: owned_only)
- namespaces = namespaces.include_route
+ namespaces = namespaces.without_project_namespaces.include_route
namespaces = namespaces.include_gitlab_subscription_with_hosted_plan if Gitlab.ee?
@@ -70,7 +70,7 @@ module API
get ':namespace/exists', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace_path = params[:namespace]
- exists = Namespace.by_parent(params[:parent_id]).filter_by_path(namespace_path).exists?
+ exists = Namespace.without_project_namespaces.by_parent(params[:parent_id]).filter_by_path(namespace_path).exists?
suggestions = exists ? [Namespace.clean_path(namespace_path)] : []
present :exists, exists
diff --git a/lib/api/package_files.rb b/lib/api/package_files.rb
index 6d0c1f44a36..79ebf18ff27 100644
--- a/lib/api/package_files.rb
+++ b/lib/api/package_files.rb
@@ -28,7 +28,10 @@ module API
package = ::Packages::PackageFinder
.new(user_project, params[:package_id]).execute
- present paginate(package.package_files), with: ::API::Entities::PackageFile
+ files = package.package_files
+ .preload_pipelines
+
+ present paginate(files), with: ::API::Entities::PackageFile
end
desc 'Remove a package file' do
diff --git a/lib/api/project_debian_distributions.rb b/lib/api/project_debian_distributions.rb
index f057251fb6b..2ba1ff85adb 100644
--- a/lib/api/project_debian_distributions.rb
+++ b/lib/api/project_debian_distributions.rb
@@ -7,14 +7,6 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- rescue_from ArgumentError do |e|
- render_api_error!(e.message, 400)
- end
-
- rescue_from ActiveRecord::RecordInvalid do |e|
- render_api_error!(e.message, 400)
- end
-
after_validation do
require_packages_enabled!
diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb
index 107311ea446..435e4bed776 100644
--- a/lib/api/project_milestones.rb
+++ b/lib/api/project_milestones.rb
@@ -7,7 +7,7 @@ module API
before { authenticate! }
- feature_category :issue_tracking
+ feature_category :team_planning
params do
requires :id, type: String, desc: 'The ID of a project'
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index bb74849a98a..9f0077d23d8 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -91,7 +91,7 @@ module API
end
def check_import_by_url_is_enabled
- forbidden! unless Gitlab::CurrentSettings.import_sources&.include?('git')
+ Gitlab::CurrentSettings.import_sources&.include?('git') || forbidden!
end
end
@@ -269,7 +269,9 @@ module API
attrs = declared_params(include_missing: false)
attrs = translate_params_for_compatibility(attrs)
filter_attributes_using_license!(attrs)
- check_import_by_url_is_enabled if params[:import_url].present?
+
+ validate_git_import_url!(params[:import_url], import_enabled: check_import_by_url_is_enabled)
+
project = ::Projects::CreateService.new(current_user, attrs).execute
if project.saved?
@@ -307,6 +309,8 @@ module API
attrs = declared_params(include_missing: false)
attrs = translate_params_for_compatibility(attrs)
filter_attributes_using_license!(attrs)
+ validate_git_import_url!(params[:import_url])
+
project = ::Projects::CreateService.new(user, attrs).execute
if project.saved?
@@ -400,7 +404,7 @@ module API
use :collection_params
use :with_custom_attributes
end
- get ':id/forks', feature_category: :source_code_management do
+ get ':id/forks', feature_category: :source_code_management, urgency: :low do
forks = ForkProjectsFinder.new(user_project, params: project_finder_params, current_user: current_user).execute
present_projects forks, request_scope: user_project
@@ -510,7 +514,7 @@ module API
end
desc 'Get languages in project repository'
- get ':id/languages', feature_category: :source_code_management do
+ get ':id/languages', feature_category: :source_code_management, urgency: :medium do
::Projects::RepositoryLanguagesService
.new(user_project, current_user)
.execute.to_h { |lang| [lang.name, lang.share] }
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index 3cebc308f51..a4f5dfefae6 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -91,7 +91,7 @@ module API
requires :name, type: String, desc: 'The name of the protected branch'
end
# rubocop: disable CodeReuse/ActiveRecord
- delete ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
+ delete ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS, urgency: :low do
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
destroy_conditionally!(protected_branch) do
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index 3b7e2b4bd27..7b89a177fd9 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -32,6 +32,7 @@ module API
optional :include_html_description, type: Boolean,
desc: 'If `true`, a response includes HTML rendered markdown of the release description.'
end
+ route_setting :authentication, job_token_allowed: true
get ':id/releases' do
releases = ::ReleasesFinder.new(user_project, current_user, declared_params.slice(:order_by, :sort)).execute
@@ -59,6 +60,7 @@ module API
optional :include_html_description, type: Boolean,
desc: 'If `true`, a response includes HTML rendered markdown of the release description.'
end
+ route_setting :authentication, job_token_allowed: true
get ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
@@ -117,6 +119,7 @@ module API
optional :released_at, type: DateTime, desc: 'The date when the release will be/was ready.'
optional :milestones, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The titles of the related milestones'
end
+ route_setting :authentication, job_token_allowed: true
put ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_update_release!
@@ -142,6 +145,7 @@ module API
params do
requires :tag_name, type: String, desc: 'The name of the tag', as: :tag
end
+ route_setting :authentication, job_token_allowed: true
delete ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_destroy_release!
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 1aa76906b3d..2dd0e40afba 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -42,6 +42,26 @@ module API
not_found! 'Blob' unless @blob
end
+
+ def fetch_target_project(current_user, user_project, params)
+ return user_project unless params[:from_project_id].present?
+
+ MergeRequestTargetProjectFinder
+ .new(current_user: current_user, source_project: user_project, project_feature: :repository)
+ .execute(include_routes: true).find_by_id(params[:from_project_id])
+ end
+
+ def compare_cache_key(current_user, user_project, target_project, params)
+ [
+ user_project,
+ target_project,
+ current_user,
+ :repository_compare,
+ target_project.repository.commit(params[:from]),
+ user_project.repository.commit(params[:to]),
+ params
+ ]
+ end
end
desc 'Get a project repository tree' do
@@ -59,7 +79,7 @@ module API
optional :page_token, type: String, desc: 'Record from which to start the keyset pagination'
end
end
- get ':id/repository/tree' do
+ get ':id/repository/tree', urgency: :low do
tree_finder = ::Repositories::TreeFinder.new(user_project, declared_params(include_missing: false))
not_found!("Tree") unless tree_finder.commit_exists?
@@ -124,22 +144,17 @@ module API
optional :from_project_id, type: String, desc: 'The project to compare from'
optional :straight, type: Boolean, desc: 'Comparison method, `true` for direct comparison between `from` and `to` (`from`..`to`), `false` to compare using merge base (`from`...`to`)', default: false
end
- get ':id/repository/compare' do
+ get ':id/repository/compare', urgency: :low do
ff_enabled = Feature.enabled?(:api_caching_rate_limit_repository_compare, user_project, default_enabled: :yaml)
+ target_project = fetch_target_project(current_user, user_project, params)
- cache_action_if(ff_enabled, [user_project, :repository_compare, current_user, declared_params], expires_in: 1.minute) do
- if params[:from_project_id].present?
- target_project = MergeRequestTargetProjectFinder
- .new(current_user: current_user, source_project: user_project, project_feature: :repository)
- .execute(include_routes: true).find_by_id(params[:from_project_id])
+ if target_project.blank?
+ render_api_error!("Target project id:#{params[:from_project_id]} is not a fork of project id:#{params[:id]}", 400)
+ end
- if target_project.blank?
- render_api_error!("Target project id:#{params[:from_project_id]} is not a fork of project id:#{params[:id]}", 400)
- end
- else
- target_project = user_project
- end
+ cache_key = compare_cache_key(current_user, user_project, target_project, declared_params)
+ cache_action_if(ff_enabled, cache_key, expires_in: 1.minute) do
compare = CompareService.new(user_project, params[:to]).execute(target_project, params[:from], straight: params[:straight])
if compare
diff --git a/lib/api/resource_milestone_events.rb b/lib/api/resource_milestone_events.rb
index aeedd7ad109..c0483ca59c2 100644
--- a/lib/api/resource_milestone_events.rb
+++ b/lib/api/resource_milestone_events.rb
@@ -8,7 +8,7 @@ module API
before { authenticate! }
{
- Issue => :issue_tracking,
+ Issue => :team_planning,
MergeRequest => :code_review
}.each do |eventable_type, feature_category|
parent_type = eventable_type.parent_class.to_s.underscore
diff --git a/lib/api/resource_state_events.rb b/lib/api/resource_state_events.rb
index 3460aa2c00e..9b6f6a954b4 100644
--- a/lib/api/resource_state_events.rb
+++ b/lib/api/resource_state_events.rb
@@ -8,7 +8,7 @@ module API
before { authenticate! }
{
- Issue => :issue_tracking,
+ Issue => :team_planning,
MergeRequest => :code_review
}.each do |eventable_class, feature_category|
eventable_name = eventable_class.to_s.underscore
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index f1ec1024492..c4b17a62b59 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -43,7 +43,7 @@ module API
params do
use :pagination
end
- get 'public' do
+ get 'public', urgency: :low do
authenticate!
present paginate(public_snippets), with: Entities::PersonalSnippet, current_user: current_user
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
index 87dc1358a51..cda30dc957f 100644
--- a/lib/api/subscriptions.rb
+++ b/lib/api/subscriptions.rb
@@ -22,21 +22,21 @@ module API
entity: Entities::Issue,
source: Project,
finder: ->(id) { find_project_issue(id) },
- feature_category: :issue_tracking
+ feature_category: :team_planning
},
{
type: 'labels',
entity: Entities::ProjectLabel,
source: Project,
finder: ->(id) { find_label(user_project, id) },
- feature_category: :issue_tracking
+ feature_category: :team_planning
},
{
type: 'labels',
entity: Entities::GroupLabel,
source: Group,
finder: ->(id) { find_label(user_group, id) },
- feature_category: :issue_tracking
+ feature_category: :team_planning
}
]
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index f018b421edd..1b37d38ef06 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -21,20 +21,28 @@ module API
optional :order_by, type: String, values: %w[name updated], default: 'updated',
desc: 'Return tags ordered by `name` or `updated` fields.'
optional :search, type: String, desc: 'Return list of tags matching the search criteria'
+ optional :page_token, type: String, desc: 'Name of tag to start the paginaition from'
use :pagination
end
- get ':id/repository/tags', feature_category: :source_code_management do
- tags, _ = ::TagsFinder.new(user_project.repository,
+ get ':id/repository/tags', feature_category: :source_code_management, urgency: :low do
+ tags_finder = ::TagsFinder.new(user_project.repository,
sort: "#{params[:order_by]}_#{params[:sort]}",
- search: params[:search]).execute
+ search: params[:search],
+ page_token: params[:page_token],
+ per_page: params[:per_page])
- paginated_tags = paginate(::Kaminari.paginate_array(tags))
+ paginated_tags = Gitlab::Pagination::GitalyKeysetPager.new(self, user_project).paginate(tags_finder)
if Feature.enabled?(:api_caching_tags, user_project, type: :development)
present_cached paginated_tags, with: Entities::Tag, project: user_project, cache_context: -> (_tag) { user_project.cache_key }
else
present paginated_tags, with: Entities::Tag, project: user_project
end
+
+ rescue Gitlab::Git::InvalidPageToken => e
+ unprocessable_entity!(e.message)
+ rescue Gitlab::Git::CommandError
+ service_unavailable!
end
desc 'Get a single repository tag' do
diff --git a/lib/api/terraform/modules/v1/packages.rb b/lib/api/terraform/modules/v1/packages.rb
index aa59b6a4fee..ad5a4ae7ea6 100644
--- a/lib/api/terraform/modules/v1/packages.rb
+++ b/lib/api/terraform/modules/v1/packages.rb
@@ -46,7 +46,8 @@ module API
def finder_params
{
package_type: :terraform_module,
- package_name: "#{params[:module_name]}/#{params[:module_system]}"
+ package_name: "#{params[:module_name]}/#{params[:module_system]}",
+ exact_name: true
}.tap do |finder_params|
finder_params[:package_version] = params[:module_version] if params.has_key?(:module_version)
end
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index e0e5ca615ac..57a6ee0bebb 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -6,7 +6,7 @@ module API
before { authenticate! }
- feature_category :issue_tracking
+ feature_category :team_planning
ISSUABLE_TYPES = {
'merge_requests' => ->(iid) { find_merge_request_with_access(iid) },
diff --git a/lib/api/topics.rb b/lib/api/topics.rb
new file mode 100644
index 00000000000..bd28ebe58a9
--- /dev/null
+++ b/lib/api/topics.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+module API
+ class Topics < ::API::Base
+ include PaginationParams
+
+ feature_category :projects
+
+ desc 'Get topics' do
+ detail 'This feature was introduced in GitLab 14.5.'
+ success Entities::Projects::Topic
+ end
+ params do
+ optional :search, type: String, desc: 'Return list of topics matching the search criteria'
+ use :pagination
+ end
+ get 'topics' do
+ topics = ::Projects::TopicsFinder.new(params: declared_params(include_missing: false)).execute
+
+ present paginate(topics), with: Entities::Projects::Topic
+ end
+
+ desc 'Get topic' do
+ detail 'This feature was introduced in GitLab 14.5.'
+ success Entities::Projects::Topic
+ end
+ params do
+ requires :id, type: Integer, desc: 'ID of project topic'
+ end
+ get 'topics/:id' do
+ topic = ::Projects::Topic.find(params[:id])
+
+ present topic, with: Entities::Projects::Topic
+ end
+
+ desc 'Create a topic' do
+ detail 'This feature was introduced in GitLab 14.5.'
+ success Entities::Projects::Topic
+ end
+ params do
+ requires :name, type: String, desc: 'Name'
+ optional :description, type: String, desc: 'Description'
+ optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for topic'
+ end
+ post 'topics' do
+ authenticated_as_admin!
+
+ topic = ::Projects::Topic.new(declared_params(include_missing: false))
+
+ if topic.save
+ present topic, with: Entities::Projects::Topic
+ else
+ render_validation_error!(topic)
+ end
+ end
+
+ desc 'Update a topic' do
+ detail 'This feature was introduced in GitLab 14.5.'
+ success Entities::Projects::Topic
+ end
+ params do
+ requires :id, type: Integer, desc: 'ID of project topic'
+ optional :name, type: String, desc: 'Name'
+ optional :description, type: String, desc: 'Description'
+ optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for topic'
+ end
+ put 'topics/:id' do
+ authenticated_as_admin!
+
+ topic = ::Projects::Topic.find(params[:id])
+
+ if topic.update(declared_params(include_missing: false))
+ present topic, with: Entities::Projects::Topic
+ else
+ render_validation_error!(topic)
+ end
+ end
+ end
+end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index f16e1148618..ce0a0e9b502 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -1062,6 +1062,7 @@ module API
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'
+ requires :credit_card_type, type: String, desc: 'The credit card network name'
end
put ":user_id/credit_card_validation", feature_category: :users do
authenticated_as_admin!
diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb
index 310054c298a..677d0840208 100644
--- a/lib/api/v3/github.rb
+++ b/lib/api/v3/github.rb
@@ -20,6 +20,9 @@ module API
# Jira Server user agent format: Jira DVCS Connector/version
JIRA_DVCS_CLOUD_USER_AGENT = 'Jira DVCS Connector Vertigo'
+ GITALY_TIMEOUT_CACHE_KEY = 'api:v3:Gitaly-timeout-cache-key'
+ GITALY_TIMEOUT_CACHE_EXPIRY = 1.day
+
include PaginationParams
feature_category :integrations
@@ -93,6 +96,32 @@ module API
notes.select { |n| n.readable_by?(current_user) }
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ # Returns an empty Array instead of the Commit diff files for a period
+ # of time after a Gitaly timeout, to mitigate frequent Gitaly timeouts
+ # for some Commit diffs.
+ def diff_files(commit)
+ return commit.diffs.diff_files unless Feature.enabled?(:api_v3_commits_skip_diff_files, commit.project, default_enabled: :yaml)
+
+ cache_key = [
+ GITALY_TIMEOUT_CACHE_KEY,
+ commit.project.id,
+ commit.cache_key
+ ].join(':')
+
+ return [] if Rails.cache.read(cache_key).present?
+
+ begin
+ commit.diffs.diff_files
+ rescue GRPC::DeadlineExceeded => error
+ # Gitaly fails to load diffs consistently for some commits. The other information
+ # is still valuable for Jira. So we skip the loading and respond with a 200 excluding diffs
+ # Remove this when https://gitlab.com/gitlab-org/gitaly/-/issues/3741 is fixed.
+ Rails.cache.write(cache_key, 1, expires_in: GITALY_TIMEOUT_CACHE_EXPIRY)
+ Gitlab::ErrorTracking.track_exception(error)
+ []
+ end
+ end
end
resource :orgs do
@@ -228,10 +257,9 @@ module API
user_project = find_project_with_access(params)
commit = user_project.commit(params[:sha])
-
not_found! 'Commit' unless commit
- present commit, with: ::API::Github::Entities::RepoCommit
+ present commit, with: ::API::Github::Entities::RepoCommit, diff_files: diff_files(commit)
end
end
end
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index 8441aeb10ab..fdce3c5ce18 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -32,7 +32,7 @@ module API
params do
optional :with_content, type: Boolean, default: false, desc: "Include pages' content"
end
- get ':id/wikis' do
+ get ':id/wikis', urgency: :low do
authorize! :read_wiki, container
entity = params[:with_content] ? Entities::WikiPage : Entities::WikiPageBasic