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.rb7
-rw-r--r--lib/api/api_guard.rb4
-rw-r--r--lib/api/applications.rb6
-rw-r--r--lib/api/broadcast_messages.rb14
-rw-r--r--lib/api/bulk_imports.rb29
-rw-r--r--lib/api/ci/helpers/runner.rb6
-rw-r--r--lib/api/ci/job_artifacts.rb38
-rw-r--r--lib/api/ci/jobs.rb4
-rw-r--r--lib/api/ci/pipeline_schedules.rb10
-rw-r--r--lib/api/ci/pipelines.rb22
-rw-r--r--lib/api/ci/runner.rb12
-rw-r--r--lib/api/ci/runners.rb12
-rw-r--r--lib/api/commits.rb8
-rw-r--r--lib/api/concerns/packages/debian_distribution_endpoints.rb12
-rw-r--r--lib/api/debian_project_packages.rb8
-rw-r--r--lib/api/deploy_tokens.rb14
-rw-r--r--lib/api/deployments.rb27
-rw-r--r--lib/api/entities/bulk_imports/entity.rb3
-rw-r--r--lib/api/entities/group_detail.rb3
-rw-r--r--lib/api/entities/merge_request_reviewer.rb12
-rw-r--r--lib/api/entities/note.rb1
-rw-r--r--lib/api/entities/project.rb31
-rw-r--r--lib/api/feature_flags.rb6
-rw-r--r--lib/api/features.rb9
-rw-r--r--lib/api/group_labels.rb31
-rw-r--r--lib/api/group_packages.rb34
-rw-r--r--lib/api/groups.rb7
-rw-r--r--lib/api/helm_packages.rb2
-rw-r--r--lib/api/helpers.rb23
-rw-r--r--lib/api/helpers/groups_helpers.rb4
-rw-r--r--lib/api/helpers/integrations_helpers.rb23
-rw-r--r--lib/api/helpers/merge_requests_helpers.rb6
-rw-r--r--lib/api/helpers/projects_helpers.rb2
-rw-r--r--lib/api/helpers/related_resources_helpers.rb4
-rw-r--r--lib/api/helpers/snippets_helpers.rb7
-rw-r--r--lib/api/internal/base.rb10
-rw-r--r--lib/api/internal/error_tracking.rb2
-rw-r--r--lib/api/internal/kubernetes.rb60
-rw-r--r--lib/api/issue_links.rb2
-rw-r--r--lib/api/issues.rb22
-rw-r--r--lib/api/labels.rb8
-rw-r--r--lib/api/markdown.rb4
-rw-r--r--lib/api/maven_packages.rb19
-rw-r--r--lib/api/members.rb6
-rw-r--r--lib/api/merge_requests.rb33
-rw-r--r--lib/api/metrics/dashboard/annotations.rb6
-rw-r--r--lib/api/metrics/user_starred_dashboards.rb4
-rw-r--r--lib/api/milestone_responses.rb6
-rw-r--r--lib/api/notes.rb7
-rw-r--r--lib/api/pages_domains.rb4
-rw-r--r--lib/api/pagination_params.rb20
-rw-r--r--lib/api/personal_access_tokens.rb11
-rw-r--r--lib/api/project_packages.rb7
-rw-r--r--lib/api/project_templates.rb2
-rw-r--r--lib/api/projects.rb6
-rw-r--r--lib/api/protected_branches.rb4
-rw-r--r--lib/api/releases.rb14
-rw-r--r--lib/api/repositories.rb10
-rw-r--r--lib/api/rubygem_packages.rb2
-rw-r--r--lib/api/search.rb25
-rw-r--r--lib/api/sidekiq_metrics.rb14
-rw-r--r--lib/api/support/git_access_actor.rb4
-rw-r--r--lib/api/topics.rb1
-rw-r--r--lib/api/unleash.rb4
-rw-r--r--lib/api/user_counts.rb8
-rw-r--r--lib/api/users.rb6
66 files changed, 539 insertions, 233 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 8827371546c..e4158eee37f 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -22,6 +22,7 @@ module API
Gitlab::GrapeLogging::Loggers::ClientEnvLogger.new,
Gitlab::GrapeLogging::Loggers::RouteLogger.new,
Gitlab::GrapeLogging::Loggers::UserLogger.new,
+ Gitlab::GrapeLogging::Loggers::TokenLogger.new,
Gitlab::GrapeLogging::Loggers::ExceptionLogger.new,
Gitlab::GrapeLogging::Loggers::QueueDurationLogger.new,
Gitlab::GrapeLogging::Loggers::PerfLogger.new,
@@ -47,6 +48,12 @@ module API
before do
header['X-Frame-Options'] = 'SAMEORIGIN'
header['X-Content-Type-Options'] = 'nosniff'
+
+ if Rails.application.config.content_security_policy && !Rails.application.config.content_security_policy_report_only
+ policy = ActionDispatch::ContentSecurityPolicy.new { |p| p.default_src :none }
+ end
+
+ request.env[ActionDispatch::ContentSecurityPolicy::Request::POLICY] = policy
end
before do
diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index 8822a30d4a1..df550f12c0d 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -48,7 +48,9 @@ module API
include Gitlab::Auth::AuthFinders
def access_token
- super || find_personal_access_token_from_http_basic_auth
+ strong_memoize(:api_guard_access_token) do
+ super || find_personal_access_token_from_http_basic_auth
+ end
end
def find_current_user!
diff --git a/lib/api/applications.rb b/lib/api/applications.rb
index 346bd6ccfe4..70488621f33 100644
--- a/lib/api/applications.rb
+++ b/lib/api/applications.rb
@@ -17,8 +17,10 @@ module API
requires :redirect_uri, type: String, desc: 'Application redirect URI'
requires :scopes, type: String, desc: 'Application scopes', allow_blank: false
- optional :confidential, type: Boolean, default: true,
- desc: 'Application will be used where the client secret is confidential'
+ optional :confidential,
+ type: Boolean,
+ default: true,
+ desc: 'Application will be used where the client secret is confidential'
end
post do
application = Doorkeeper::Application.new(declared_params)
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index e818cad0d03..6af7c3b4804 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -37,8 +37,11 @@ module API
optional :ends_at, type: DateTime, desc: 'Ending time', default: -> { 1.hour.from_now }
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
- optional :target_access_levels, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
- values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS, desc: 'Target user roles'
+ optional :target_access_levels,
+ type: Array[Integer],
+ coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS,
+ desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast type. Defaults to banner', default: -> { 'banner' }
optional :dismissable, type: Boolean, desc: 'Is dismissable'
@@ -79,8 +82,11 @@ module API
optional :ends_at, type: DateTime, desc: 'Ending time'
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
- optional :target_access_levels, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
- values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS, desc: 'Target user roles'
+ optional :target_access_levels,
+ type: Array[Integer],
+ coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS,
+ desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast Type'
optional :dismissable, type: Boolean, desc: 'Is dismissable'
diff --git a/lib/api/bulk_imports.rb b/lib/api/bulk_imports.rb
index b1cb84c97cb..2c6adc0f37b 100644
--- a/lib/api/bulk_imports.rb
+++ b/lib/api/bulk_imports.rb
@@ -44,14 +44,29 @@ module API
requires :access_token, type: String, desc: 'Access token to the source GitLab instance'
end
requires :entities, type: Array, desc: 'List of entities to import' do
- requires :source_type, type: String, desc: 'Source entity type (only `group_entity` is supported)',
+ requires :source_type,
+ type: String,
+ desc: 'Source entity type (only `group_entity` is supported)',
values: %w[group_entity]
requires :source_full_path, type: String, desc: 'Source full path of the entity to import'
- requires :destination_name, type: String, desc: 'Destination slug for the entity'
requires :destination_namespace, type: String, desc: 'Destination namespace for the entity'
+ optional :destination_slug, type: String, desc: 'Destination slug for the entity'
+ optional :destination_name,
+ type: String,
+ desc: 'Deprecated: Use :destination_slug instead. Destination slug for the entity'
+
+ mutually_exclusive :destination_slug, :destination_name
+ at_least_one_of :destination_slug, :destination_name
end
end
post do
+ params[:entities].each do |entity|
+ if entity[:destination_name]
+ entity[:destination_slug] ||= entity[:destination_name]
+ entity.delete(:destination_name)
+ end
+ end
+
response = ::BulkImports::CreateService.new(
current_user,
params[:entities],
@@ -72,9 +87,9 @@ module API
params do
use :pagination
optional :sort, type: String, values: %w[asc desc], default: 'desc',
- desc: 'Return GitLab Migrations sorted in created by `asc` or `desc` order.'
+ desc: 'Return GitLab Migrations sorted in created by `asc` or `desc` order.'
optional :status, type: String, values: BulkImport.all_human_statuses,
- desc: 'Return GitLab Migrations with specified status'
+ desc: 'Return GitLab Migrations with specified status'
end
get do
present paginate(bulk_imports), with: Entities::BulkImport
@@ -86,9 +101,9 @@ module API
params do
use :pagination
optional :sort, type: String, values: %w[asc desc], default: 'desc',
- desc: 'Return GitLab Migrations sorted in created by `asc` or `desc` order.'
+ desc: 'Return GitLab Migrations sorted in created by `asc` or `desc` order.'
optional :status, type: String, values: ::BulkImports::Entity.all_human_statuses,
- desc: "Return all GitLab Migrations' entities with specified status"
+ desc: "Return all GitLab Migrations' entities with specified status"
end
get :entities do
entities = ::BulkImports::EntitiesFinder.new(
@@ -115,7 +130,7 @@ module API
params do
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
optional :status, type: String, values: ::BulkImports::Entity.all_human_statuses,
- desc: 'Return import entities with specified status'
+ desc: 'Return import entities with specified status'
use :pagination
end
get ':import_id/entities' do
diff --git a/lib/api/ci/helpers/runner.rb b/lib/api/ci/helpers/runner.rb
index fe49074afed..269f2fa7ddc 100644
--- a/lib/api/ci/helpers/runner.rb
+++ b/lib/api/ci/helpers/runner.rb
@@ -138,17 +138,13 @@ module API
def set_application_context
return unless current_job
- Gitlab::ApplicationContext.push(job: current_job)
+ Gitlab::ApplicationContext.push(job: current_job, runner: current_runner)
end
def track_ci_minutes_usage!(_build, _runner)
# noop: overridden in EE
end
- def log_artifact_size(artifact)
- Gitlab::ApplicationContext.push(artifact: artifact)
- end
-
private
def get_runner_config_from_request
diff --git a/lib/api/ci/job_artifacts.rb b/lib/api/ci/job_artifacts.rb
index 8b332f96be0..b843404e9d7 100644
--- a/lib/api/ci/job_artifacts.rb
+++ b/lib/api/ci/job_artifacts.rb
@@ -30,15 +30,16 @@ module API
requires :job, type: String, desc: 'The name for the job'
end
route_setting :authentication, job_token_allowed: true
- get ':id/jobs/artifacts/:ref_name/download', urgency: :low,
- requirements: { ref_name: /.+/ } do
- authorize_download_artifacts!
+ get ':id/jobs/artifacts/:ref_name/download',
+ urgency: :low,
+ requirements: { ref_name: /.+/ } do
+ authorize_download_artifacts!
- latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
- authorize_read_job_artifacts!(latest_build)
+ latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
+ authorize_read_job_artifacts!(latest_build)
- present_artifacts_file!(latest_build.artifacts_file)
- end
+ present_artifacts_file!(latest_build.artifacts_file)
+ end
desc 'Download a specific file from artifacts archive from a ref' do
detail 'This feature was introduced in GitLab 11.5'
@@ -49,21 +50,22 @@ module API
requires :artifact_path, type: String, desc: 'Artifact path'
end
route_setting :authentication, job_token_allowed: true
- get ':id/jobs/artifacts/:ref_name/raw/*artifact_path', urgency: :low,
- format: false,
- requirements: { ref_name: /.+/ } do
- authorize_download_artifacts!
+ get ':id/jobs/artifacts/:ref_name/raw/*artifact_path',
+ urgency: :low,
+ format: false,
+ requirements: { ref_name: /.+/ } do
+ authorize_download_artifacts!
- build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
- authorize_read_job_artifacts!(build)
+ build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
+ authorize_read_job_artifacts!(build)
- path = Gitlab::Ci::Build::Artifacts::Path
- .new(params[:artifact_path])
+ path = Gitlab::Ci::Build::Artifacts::Path
+ .new(params[:artifact_path])
- bad_request! unless path.valid?
+ bad_request! unless path.valid?
- send_artifacts_entry(build.artifacts_file, path)
- end
+ send_artifacts_entry(build.artifacts_file, path)
+ end
desc 'Download the artifacts archive from a job' do
detail 'This feature was introduced in GitLab 8.5'
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index 97471d3c96e..cd5f1f77ced 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -152,8 +152,8 @@ module API
end
params do
requires :job_id, type: Integer, desc: 'The ID of a Job'
- optional :job_variables_attributes, type: Array,
- desc: 'User defined variables that will be included when running the job' do
+ optional :job_variables_attributes,
+ type: Array, desc: 'User defined variables that will be included when running the job' do
requires :key, type: String, desc: 'The name of the variable'
requires :value, type: String, desc: 'The value of the variable'
end
diff --git a/lib/api/ci/pipeline_schedules.rb b/lib/api/ci/pipeline_schedules.rb
index 4b522f37524..886c3509c51 100644
--- a/lib/api/ci/pipeline_schedules.rb
+++ b/lib/api/ci/pipeline_schedules.rb
@@ -42,6 +42,16 @@ module API
present pipeline_schedule, with: Entities::Ci::PipelineScheduleDetails, user: current_user
end
+ desc 'Get all pipelines triggered from a pipeline schedule' do
+ success Entities::Ci::PipelineBasic
+ end
+ params do
+ requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule ID'
+ end
+ get ':id/pipeline_schedules/:pipeline_schedule_id/pipelines' do
+ present paginate(pipeline_schedule.pipelines), with: Entities::Ci::PipelineBasic
+ end
+
desc 'Create a new pipeline schedule' do
success Entities::Ci::PipelineScheduleDetails
end
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index cd686a28dd2..72a81330e71 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -21,17 +21,17 @@ module API
helpers do
params :optional_scope do
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
- values: ::CommitStatus::AVAILABLE_STATUSES,
- coerce_with: ->(scope) {
- case scope
- when String
- [scope]
- when ::Array
- scope
- else
- ['unknown']
- end
- }
+ values: ::CommitStatus::AVAILABLE_STATUSES,
+ coerce_with: ->(scope) {
+ case scope
+ when String
+ [scope]
+ when ::Array
+ scope
+ else
+ ['unknown']
+ end
+ }
end
end
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 65dc002e67d..9e4a700d0f3 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -38,7 +38,8 @@ module API
attributes[:maintenance_note] ||= deprecated_note if deprecated_note
attributes[:active] = !attributes.delete(:paused) if attributes.include?(:paused)
- @runner = ::Ci::Runners::RegisterRunnerService.new.execute(params[:token], attributes)
+ result = ::Ci::Runners::RegisterRunnerService.new.execute(params[:token], attributes)
+ @runner = result.success? ? result.payload[:runner] : nil
forbidden! unless @runner
if @runner.persisted?
@@ -255,7 +256,7 @@ module API
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
+ default: 'archive', values: ::Ci::JobArtifact.file_types.keys
end
post '/:id/artifacts/authorize', feature_category: :build_artifacts, urgency: :low do
not_allowed! unless Gitlab.config.artifacts.enabled
@@ -288,9 +289,9 @@ module API
optional :token, type: String, desc: %q(Job's authentication token)
optional :expire_in, type: String, desc: %q(Specify when artifacts should expire)
optional :artifact_type, type: String, desc: %q(The type of artifact),
- default: 'archive', values: ::Ci::JobArtifact.file_types.keys
+ default: 'archive', values: ::Ci::JobArtifact.file_types.keys
optional :artifact_format, type: String, desc: %q(The format of artifact),
- default: 'zip', values: ::Ci::JobArtifact.file_formats.keys
+ default: 'zip', values: ::Ci::JobArtifact.file_formats.keys
optional :metadata, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact metadata to store (generated by Multipart middleware))
end
post '/:id/artifacts', feature_category: :build_artifacts, urgency: :low do
@@ -305,7 +306,8 @@ module API
result = ::Ci::JobArtifacts::CreateService.new(job).execute(artifacts, params, metadata_file: metadata)
if result[:status] == :success
- log_artifact_size(result[:artifact])
+ log_artifacts_filesize(result[:artifact])
+
status :created
body "201"
else
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 06bfee59140..ec9b09a3419 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -16,7 +16,7 @@ module API
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
- desc: 'The scope of specific runners to show'
+ desc: 'The scope of specific runners to show'
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
@@ -38,7 +38,7 @@ module API
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
- desc: 'The scope of specific runners to show'
+ desc: 'The scope of specific runners to show'
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
@@ -159,7 +159,7 @@ module API
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
- desc: 'The scope of specific runners to show'
+ desc: 'The scope of specific runners to show'
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
@@ -188,7 +188,7 @@ module API
runner = get_runner(params[:runner_id])
authenticate_enable_runner!(runner)
- if ::Ci::Runners::AssignRunnerService.new(runner, user_project, current_user).execute
+ if ::Ci::Runners::AssignRunnerService.new(runner, user_project, current_user).execute.success?
present runner, with: Entities::Ci::Runner
else
render_validation_error!(runner)
@@ -225,10 +225,10 @@ module API
end
params do
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
- desc: 'The type of the runners to show'
+ desc: 'The type of the runners to show'
optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
- desc: 'The status of the runners to show'
+ desc: 'The status of the runners to show'
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
use :pagination
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 5fd9a8e3278..7a6c3e4d53f 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -10,6 +10,8 @@ module API
before do
require_repository_enabled!
authorize! :download_code, user_project
+
+ verify_pagination_params!
end
helpers do
@@ -86,7 +88,7 @@ module API
requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide either `start_branch` or `start_sha`, and optionally `start_project`.', allow_blank: false
requires :commit_message, type: String, desc: 'Commit message'
requires :actions, type: Array, desc: 'Actions to perform in commit' do
- requires :action, type: String, desc: 'The action to perform, `create`, `delete`, `move`, `update`, `chmod`', values: %w[create update move delete chmod].freeze
+ requires :action, type: String, desc: 'The action to perform, `create`, `delete`, `move`, `update`, `chmod`', values: %w[create update move delete chmod].freeze, allow_blank: false
requires :file_path, type: String, desc: 'Full path to the file. Ex. `lib/class.rb`'
given action: ->(action) { action == 'move' } do
requires :previous_path, type: String, desc: 'Original full path to the file being moved. Ex. `lib/class1.rb`'
@@ -302,8 +304,8 @@ module API
not_found!('Commit') unless commit
refs = []
- refs.concat(user_project.repository.branch_names_contains(commit.id).map {|name| { type: 'branch', name: name }}) unless params[:type] == 'tag'
- refs.concat(user_project.repository.tag_names_contains(commit.id).map {|name| { type: 'tag', name: name }}) unless params[:type] == 'branch'
+ refs.concat(user_project.repository.branch_names_contains(commit.id).map { |name| { type: 'branch', name: name } }) unless params[:type] == 'tag'
+ refs.concat(user_project.repository.tag_names_contains(commit.id).map { |name| { type: 'tag', name: name } }) unless params[:type] == 'branch'
refs = Kaminari.paginate_array(refs)
present paginate(refs), with: Entities::BasicRef
diff --git a/lib/api/concerns/packages/debian_distribution_endpoints.rb b/lib/api/concerns/packages/debian_distribution_endpoints.rb
index e01f3adbb06..380966136df 100644
--- a/lib/api/concerns/packages/debian_distribution_endpoints.rb
+++ b/lib/api/concerns/packages/debian_distribution_endpoints.rb
@@ -33,13 +33,13 @@ module API
optional :valid_time_duration_seconds, type: Integer, desc: 'The duration before the Release file should be considered expired by the client'
optional :components, type: Array[String],
- coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
- regexp: Gitlab::Regex.debian_component_regex,
- desc: 'The list of Components'
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ regexp: Gitlab::Regex.debian_component_regex,
+ desc: 'The list of Components'
optional :architectures, type: Array[String],
- coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
- regexp: Gitlab::Regex.debian_architecture_regex,
- desc: 'The list of Architectures'
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ regexp: Gitlab::Regex.debian_architecture_regex,
+ desc: 'The list of Architectures'
end
end
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
index ca576254c3d..06846d8f36e 100644
--- a/lib/api/debian_project_packages.rb
+++ b/lib/api/debian_project_packages.rb
@@ -73,10 +73,10 @@ module API
bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:debian_max_file_size, params[:file].size)
file_params = {
- file: params['file'],
- file_name: params['file_name'],
- file_sha1: params['file.sha1'],
- file_md5: params['file.md5']
+ file: params['file'],
+ file_name: params['file_name'],
+ file_sha1: params['file.sha1'],
+ file_md5: params['file.md5']
}
package = ::Packages::Debian::FindOrCreateIncomingService.new(authorized_user_project, current_user).execute
diff --git a/lib/api/deploy_tokens.rb b/lib/api/deploy_tokens.rb
index 3e0411d2e91..3955e29621f 100644
--- a/lib/api/deploy_tokens.rb
+++ b/lib/api/deploy_tokens.rb
@@ -71,8 +71,11 @@ module API
params do
requires :name, type: String, desc: "New deploy token's name"
- requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
- desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", "write_registry", "read_package_registry", or "write_package_registry".'
+ requires :scopes,
+ type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
+ desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", "write_registry", "read_package_registry", or "write_package_registry".'
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
end
@@ -152,8 +155,11 @@ module API
params do
requires :name, type: String, desc: 'The name of the deploy token'
- requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
- desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", "write_registry", "read_package_registry", or "write_package_registry".'
+ requires :scopes,
+ type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
+ desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", "write_registry", "read_package_registry", or "write_package_registry".'
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
end
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index 8db5f54b45a..ee0a026d7ac 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -119,9 +119,9 @@ module API
end
params do
requires :status,
- type: String,
- desc: 'The new status of the deployment',
- values: %w[running success failed canceled]
+ type: String,
+ desc: 'The new status of the deployment',
+ values: %w[running success failed canceled]
end
put ':id/deployments/:deployment_id' do
authorize!(:read_deployment, user_project)
@@ -143,6 +143,27 @@ module API
end
end
+ desc 'Deletes an existing deployment' do
+ detail 'This feature was introduced in GitLab 15.3'
+ http_codes [[204, 'Deployment was deleted'], [403, 'Forbidden'], [400, 'Cannot destroy']]
+ end
+ params do
+ requires :deployment_id, type: Integer, desc: 'The deployment ID'
+ end
+ delete ':id/deployments/:deployment_id' do
+ deployment = user_project.deployments.find(params[:deployment_id])
+
+ authorize!(:destroy_deployment, deployment)
+
+ destroy_conditionally!(deployment) do
+ result = ::Ci::Deployments::DestroyService.new(user_project, current_user).execute(deployment)
+
+ if result[:status] == :error
+ render_api_error!(result[:message], result[:http_status] || 400)
+ end
+ end
+ end
+
helpers Helpers::MergeRequestsHelpers
desc 'Get all merge requests of a deployment' do
diff --git a/lib/api/entities/bulk_imports/entity.rb b/lib/api/entities/bulk_imports/entity.rb
index e8c31256b17..142bfaf2149 100644
--- a/lib/api/entities/bulk_imports/entity.rb
+++ b/lib/api/entities/bulk_imports/entity.rb
@@ -8,7 +8,8 @@ module API
expose :bulk_import_id
expose :status_name, as: :status
expose :source_full_path
- expose :destination_name
+ expose :destination_name # deprecated
+ expose :destination_slug
expose :destination_namespace
expose :parent_id
expose :namespace_id
diff --git a/lib/api/entities/group_detail.rb b/lib/api/entities/group_detail.rb
index e521de0d572..7b05984421a 100644
--- a/lib/api/entities/group_detail.rb
+++ b/lib/api/entities/group_detail.rb
@@ -7,7 +7,8 @@ module API
SharedGroupWithGroup.represent(group.shared_with_group_links_visible_to_user(options[:current_user]))
end
expose :runners_token, if: ->(_, options) { options[:user_can_admin_group] }
- expose :prevent_sharing_groups_outside_hierarchy, if: ->(group) { group.root? }
+ expose :prevent_sharing_groups_outside_hierarchy,
+ if: ->(group) { group.root? && group.namespace_settings.present? }
expose :projects,
if: ->(_, options) { options[:with_projects] },
diff --git a/lib/api/entities/merge_request_reviewer.rb b/lib/api/entities/merge_request_reviewer.rb
new file mode 100644
index 00000000000..3bf2ccc36aa
--- /dev/null
+++ b/lib/api/entities/merge_request_reviewer.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class MergeRequestReviewer < Grape::Entity
+ expose :reviewer, as: :user, using: Entities::UserBasic
+ expose :updated_state_by, using: Entities::UserBasic
+ expose :state
+ expose :created_at
+ end
+ end
+end
diff --git a/lib/api/entities/note.rb b/lib/api/entities/note.rb
index a597aa7bb4a..a92f534bbdc 100644
--- a/lib/api/entities/note.rb
+++ b/lib/api/entities/note.rb
@@ -26,6 +26,7 @@ module API
expose :resolved_at, if: ->(note, options) { note.resolvable? }
expose :confidential?, as: :confidential
+ expose :confidential?, as: :internal
# Avoid N+1 queries as much as possible
expose(:noteable_iid) { |note| note.noteable.iid if NOTEABLE_TYPES_WITH_IID.include?(note.noteable_type) }
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 906c252d7f9..1739bdd639e 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -47,8 +47,9 @@ module API
expose :visibility
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :resolve_outdated_diff_discussions
- expose :container_expiration_policy, using: Entities::ContainerExpirationPolicy,
- if: -> (project, _) { project.container_expiration_policy }
+ expose :container_expiration_policy,
+ using: Entities::ContainerExpirationPolicy,
+ if: -> (project, _) { project.container_expiration_policy }
# Expose old field names with the new permissions methods to keep API compatible
# TODO: remove in API v5, replaced by *_access_level
@@ -67,18 +68,18 @@ module API
Ability.allowed?(options[:current_user], :create_merge_request_in, project)
end
- expose(:issues_access_level) { |project, options| project.project_feature.string_access_level(:issues) }
- expose(:repository_access_level) { |project, options| project.project_feature.string_access_level(:repository) }
- expose(:merge_requests_access_level) { |project, options| project.project_feature.string_access_level(:merge_requests) }
- expose(:forking_access_level) { |project, options| project.project_feature.string_access_level(:forking) }
- expose(:wiki_access_level) { |project, options| project.project_feature.string_access_level(:wiki) }
- expose(:builds_access_level) { |project, options| project.project_feature.string_access_level(:builds) }
- expose(:snippets_access_level) { |project, options| project.project_feature.string_access_level(:snippets) }
- expose(:pages_access_level) { |project, options| project.project_feature.string_access_level(:pages) }
- expose(:operations_access_level) { |project, options| project.project_feature.string_access_level(:operations) }
- expose(:analytics_access_level) { |project, options| project.project_feature.string_access_level(:analytics) }
- expose(:container_registry_access_level) { |project, options| project.project_feature.string_access_level(:container_registry) }
- expose(:security_and_compliance_access_level) { |project, options| project.project_feature.string_access_level(:security_and_compliance) }
+ expose(:issues_access_level) { |project, options| project_feature_string_access_level(project, :issues) }
+ expose(:repository_access_level) { |project, options| project_feature_string_access_level(project, :repository) }
+ expose(:merge_requests_access_level) { |project, options| project_feature_string_access_level(project, :merge_requests) }
+ expose(:forking_access_level) { |project, options| project_feature_string_access_level(project, :forking) }
+ expose(:wiki_access_level) { |project, options| project_feature_string_access_level(project, :wiki) }
+ expose(:builds_access_level) { |project, options| project_feature_string_access_level(project, :builds) }
+ expose(:snippets_access_level) { |project, options| project_feature_string_access_level(project, :snippets) }
+ expose(:pages_access_level) { |project, options| project_feature_string_access_level(project, :pages) }
+ expose(:operations_access_level) { |project, options| project_feature_string_access_level(project, :operations) }
+ expose(:analytics_access_level) { |project, options| project_feature_string_access_level(project, :analytics) }
+ expose(:container_registry_access_level) { |project, options| project_feature_string_access_level(project, :container_registry) }
+ expose(:security_and_compliance_access_level) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
expose :emails_disabled
expose :shared_runners_enabled
@@ -105,13 +106,13 @@ module API
expose :ci_job_token_scope_enabled
expose :ci_separated_caches
expose :ci_opt_in_jwt
+ expose :ci_allow_fork_pipelines_to_run_in_parent_project
expose :public_builds, as: :public_jobs
expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
project.build_allow_git_fetch ? 'fetch' : 'clone'
end
expose :build_timeout
expose :auto_cancel_pending_pipelines
- 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|
user = options[:current_user]
diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb
index 42050888c14..67e96284449 100644
--- a/lib/api/feature_flags.rb
+++ b/lib/api/feature_flags.rb
@@ -24,8 +24,10 @@ module API
success ::API::Entities::FeatureFlag
end
params do
- optional :scope, type: String, desc: 'The scope of feature flags',
- values: %w[enabled disabled]
+ optional :scope,
+ type: String,
+ desc: 'The scope of feature flags',
+ values: %w[enabled disabled]
use :pagination
end
get do
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 13a6aedc2df..f89da48acea 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -69,11 +69,14 @@ module API
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 or comma-separated multiple usernames'
- optional :group, type: String,
+ optional :group,
+ type: String,
desc: "A GitLab group's path, such as 'gitlab-org', or comma-separated multiple group paths"
- optional :namespace, type: String,
+ optional :namespace,
+ type: String,
desc: "A GitLab group or user namespace path, such as 'john-doe', or comma-separated multiple namespace paths"
- optional :project, type: String,
+ optional :project,
+ type: String,
desc: "A projects path, such as `gitlab-org/gitlab-ce`, or comma-separated multiple project paths"
optional :force, type: Boolean, desc: 'Skip feature flag validation checks, ie. YAML definition'
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index e4cbe442f58..a8c48a6f4fe 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -19,15 +19,24 @@ module API
success Entities::GroupLabel
end
params do
- optional :with_counts, type: Boolean, default: false,
+ optional :with_counts,
+ type: Boolean,
+ default: false,
desc: 'Include issue and merge request counts'
- optional :include_ancestor_groups, type: Boolean, default: true,
+ optional :include_ancestor_groups,
+ type: Boolean,
+ default: true,
desc: 'Include ancestor groups'
- optional :include_descendant_groups, type: Boolean, default: false,
+ optional :include_descendant_groups,
+ type: Boolean,
+ default: false,
desc: 'Include descendant groups. This feature was added in GitLab 13.6'
- optional :only_group_labels, type: Boolean, default: true,
+ optional :only_group_labels,
+ type: Boolean,
+ default: true,
desc: 'Toggle to include only group labels or also project labels. This feature was added in GitLab 13.6'
- optional :search, type: String,
+ optional :search,
+ type: String,
desc: 'Keyword to filter labels by. This feature was added in GitLab 13.6'
use :pagination
end
@@ -40,11 +49,17 @@ module API
success Entities::GroupLabel
end
params do
- optional :include_ancestor_groups, type: Boolean, default: true,
+ optional :include_ancestor_groups,
+ type: Boolean,
+ default: true,
desc: 'Include ancestor groups'
- optional :include_descendant_groups, type: Boolean, default: false,
+ optional :include_descendant_groups,
+ type: Boolean,
+ default: false,
desc: 'Include descendant groups. This feature was added in GitLab 13.6'
- optional :only_group_labels, type: Boolean, default: true,
+ optional :only_group_labels,
+ type: Boolean,
+ default: true,
desc: 'Toggle to include only group labels or also project labels. This feature was added in GitLab 13.6'
end
get ':id/labels/:name' do
diff --git a/lib/api/group_packages.rb b/lib/api/group_packages.rb
index af6e2b1e422..72d67b41c31 100644
--- a/lib/api/group_packages.rb
+++ b/lib/api/group_packages.rb
@@ -24,17 +24,29 @@ module API
end
params do
use :pagination
- optional :order_by, type: String, values: %w[created_at name version type project_path], default: 'created_at',
- desc: 'Return packages ordered by `created_at`, `name`, `version` or `type` fields.'
- optional :sort, type: String, values: %w[asc desc], default: 'asc',
- desc: 'Return packages sorted in `asc` or `desc` order.'
- optional :package_type, type: String, values: Packages::Package.package_types.keys,
- desc: 'Return packages of a certain type'
- optional :package_name, type: String,
- desc: 'Return packages with this name'
- optional :include_versionless, type: Boolean,
- desc: 'Returns packages without a version'
- optional :status, type: String, values: Packages::Package.statuses.keys,
+ optional :order_by,
+ type: String,
+ values: %w[created_at name version type project_path],
+ default: 'created_at',
+ desc: 'Return packages ordered by `created_at`, `name`, `version` or `type` fields.'
+ optional :sort,
+ type: String,
+ values: %w[asc desc],
+ default: 'asc',
+ desc: 'Return packages sorted in `asc` or `desc` order.'
+ optional :package_type,
+ type: String,
+ values: Packages::Package.package_types.keys,
+ desc: 'Return packages of a certain type'
+ optional :package_name,
+ type: String,
+ desc: 'Return packages with this name'
+ optional :include_versionless,
+ type: Boolean,
+ desc: 'Returns packages without a version'
+ optional :status,
+ type: String,
+ values: Packages::Package.statuses.keys,
desc: 'Return packages with specified status'
end
get ':id/packages' do
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index b63396ed073..82bbab5d7d4 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -394,9 +394,10 @@ module API
desc 'Transfer a group to a new parent group or promote a subgroup to a root group'
params do
- optional :group_id, type: Integer,
- desc: 'The ID of the target group to which the group needs to be transferred to.'\
- 'If not provided, the source group will be promoted to a root group.'
+ optional :group_id,
+ type: Integer,
+ desc: 'The ID of the target group to which the group needs to be transferred to.'\
+ 'If not provided, the source group will be promoted to a root group.'
end
post ':id/transfer', feature_category: :subgroups do
group = find_group!(params[:id])
diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb
index e0e4e02fa55..a1b265bc8f3 100644
--- a/lib/api/helm_packages.rb
+++ b/lib/api/helm_packages.rb
@@ -100,7 +100,7 @@ module API
).execute(:helm, name: ::Packages::Helm::TEMPORARY_PACKAGE_NAME)
chart_params = {
- file: params[:chart],
+ file: params[:chart],
file_name: PACKAGE_FILENAME
}
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index fdb0fbf820d..1d0f0c6e7bb 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -6,11 +6,13 @@ module API
include Helpers::Caching
include Helpers::Pagination
include Helpers::PaginationStrategies
+ include Gitlab::Ci::Artifacts::Logger
SUDO_HEADER = "HTTP_SUDO"
GITLAB_SHARED_SECRET_HEADER = "Gitlab-Shared-Secret"
SUDO_PARAM = :sudo
API_USER_ENV = 'gitlab.api.user'
+ API_TOKEN_ENV = 'gitlab.api.token'
API_EXCEPTION_ENV = 'gitlab.api.exception'
API_RESPONSE_STATUS_CODE = 'gitlab.api.response_status_code'
@@ -20,7 +22,11 @@ module API
end
def check_unmodified_since!(last_modified)
- if_unmodified_since = Time.parse(headers['If-Unmodified-Since']) rescue nil
+ if_unmodified_since = begin
+ Time.parse(headers['If-Unmodified-Since'])
+ rescue StandardError
+ nil
+ end
if if_unmodified_since && last_modified && last_modified > if_unmodified_since
render_api_error!('412 Precondition Failed', 412)
@@ -74,6 +80,8 @@ module API
save_current_user_in_env(@current_user) if @current_user
+ save_current_token_in_env
+
if @current_user
::ApplicationRecord
.sticking
@@ -88,6 +96,13 @@ module API
env[API_USER_ENV] = { user_id: user.id, username: user.username }
end
+ def save_current_token_in_env
+ token = access_token
+ env[API_TOKEN_ENV] = { token_id: token.id, token_type: token.class } if token
+
+ rescue Gitlab::Auth::UnauthorizedError
+ end
+
def sudo?
initial_current_user != current_user
end
@@ -574,12 +589,8 @@ module API
end
end
- def log_artifact_file_size(file)
- Gitlab::ApplicationContext.push(artifact: file.model)
- end
-
def present_artifacts_file!(file, **args)
- log_artifact_file_size(file) if file
+ log_artifacts_filesize(file&.model)
present_carrierwave_file!(file, **args)
end
diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb
index 72bdb32d38c..2b10eebb009 100644
--- a/lib/api/helpers/groups_helpers.rb
+++ b/lib/api/helpers/groups_helpers.rb
@@ -9,8 +9,8 @@ module API
params :optional_params_ce do
optional :description, type: String, desc: 'The description of the group'
optional :visibility, type: String,
- values: Gitlab::VisibilityLevel.string_values,
- desc: 'The visibility of the group'
+ values: Gitlab::VisibilityLevel.string_values,
+ desc: 'The visibility of the group'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :avatar, type: File, desc: 'Avatar image for the group' # rubocop:disable Scalability/FileUploads
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index 0b0100c7d7f..99273e81730 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -328,14 +328,12 @@ module API
type: String,
desc: '(Advanced) The full URL for your Datadog site'
},
- # TODO: uncomment this field once :datadog_integration_logs_collection is rolled out
- # https://gitlab.com/gitlab-org/gitlab/-/issues/346339
- # {
- # required: false,
- # name: :archive_trace_events,
- # type: Boolean,
- # desc: 'When enabled, job logs will be collected by Datadog and shown along pipeline execution traces'
- # },
+ {
+ required: false,
+ name: :archive_trace_events,
+ type: Boolean,
+ desc: 'When enabled, job logs will be collected by Datadog and shown along pipeline execution traces'
+ },
{
required: false,
name: :datadog_service,
@@ -678,6 +676,15 @@ module API
desc: 'Contents of the credentials.json file of your service account, like: { "type": "service_account", "project_id": ... }'
}
],
+ 'pumble' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The Pumble chat webhook. For example, https://api.pumble.com/workspaces/x/...'
+ },
+ chat_notification_events
+ ].flatten,
'pushover' => [
{
required: true,
diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb
index 00d9f49adf0..85648cd166d 100644
--- a/lib/api/helpers/merge_requests_helpers.rb
+++ b/lib/api/helpers/merge_requests_helpers.rb
@@ -17,7 +17,9 @@ module API
types: [Integer, String],
integer_none_any: true,
desc: 'Return merge requests which are assigned to the user with the given ID'
- optional :assignee_username, type: Array[String], check_assignees_count: true,
+ optional :assignee_username,
+ type: Array[String],
+ check_assignees_count: true,
coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
desc: 'Return merge requests which are assigned to the user with the given username'
mutually_exclusive :assignee_id, :assignee_username
@@ -129,7 +131,7 @@ module API
end
def self.sort_options_help
- sort_options.map {|y| "`#{y}`" }.to_sentence(last_word_connector: ' or ')
+ sort_options.map { |y| "`#{y}`" }.to_sentence(last_word_connector: ' or ')
end
end
end
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 3a518959b2c..628182ad1ab 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -96,6 +96,7 @@ module API
params :optional_update_params_ce do
optional :ci_forward_deployment_enabled, type: Boolean, desc: 'Skip older deployment jobs that are still pending'
+ optional :ci_allow_fork_pipelines_to_run_in_parent_project, type: Boolean, desc: 'Allow fork merge request pipelines to run in parent project'
optional :ci_separated_caches, type: Boolean, desc: 'Enable or disable separated caches based on branch protection.'
optional :restrict_user_defined_variables, type: Boolean, desc: 'Restrict use of user-defined variables when triggering a pipeline'
end
@@ -130,6 +131,7 @@ module API
:builds_access_level,
:ci_config_path,
:ci_default_git_depth,
+ :ci_allow_fork_pipelines_to_run_in_parent_project,
:ci_forward_deployment_enabled,
:ci_separated_caches,
:container_registry_access_level,
diff --git a/lib/api/helpers/related_resources_helpers.rb b/lib/api/helpers/related_resources_helpers.rb
index d0eda68bf52..27fcc0a68fb 100644
--- a/lib/api/helpers/related_resources_helpers.rb
+++ b/lib/api/helpers/related_resources_helpers.rb
@@ -9,6 +9,10 @@ module API
available?(:issues, project, options[:current_user])
end
+ def project_feature_string_access_level(project, feature)
+ project.project_feature&.string_access_level(feature)
+ end
+
def mrs_available?(project, options)
available?(:merge_requests, project, options[:current_user])
end
diff --git a/lib/api/helpers/snippets_helpers.rb b/lib/api/helpers/snippets_helpers.rb
index 2d8c761101a..fe20fb3cbe2 100644
--- a/lib/api/helpers/snippets_helpers.rb
+++ b/lib/api/helpers/snippets_helpers.rb
@@ -29,9 +29,10 @@ module API
params :update_file_params do |options|
optional :files, type: Array, desc: 'An array of files to update' do
- requires :action, type: String,
- values: SnippetInputAction::ACTIONS.map(&:to_s),
- desc: "The type of action to perform on the file, must be one of: #{SnippetInputAction::ACTIONS.join(", ")}"
+ requires :action,
+ type: String,
+ values: SnippetInputAction::ACTIONS.map(&:to_s),
+ desc: "The type of action to perform on the file, must be one of: #{SnippetInputAction::ACTIONS.join(", ")}"
optional :content, type: String, desc: 'The content of a snippet'
optional :file_path, file_path: true, type: String, desc: 'The file path of a snippet file'
optional :previous_path, file_path: true, type: String, desc: 'The previous path of a snippet file'
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index b53f855c3a2..6f475fa8d74 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -39,6 +39,7 @@ module API
container.lfs_http_url_to_repo
end
+ # rubocop: disable Metrics/AbcSize
def check_allowed(params)
# This is a separate method so that EE can alter its behaviour more
# easily.
@@ -47,6 +48,14 @@ module API
check_rate_limit!(:gitlab_shell_operation, scope: [params[:action], params[:project], actor.key_or_user])
end
+ if Feature.enabled?(:rate_limit_gitlab_shell_by_ip, actor.user)
+ rate_limiter = Gitlab::Auth::IpRateLimiter.new(request.ip)
+
+ unless rate_limiter.trusted_ip?
+ check_rate_limit!(:gitlab_shell_operation, scope: [params[:action], params[:project], rate_limiter.ip])
+ end
+ end
+
# Stores some Git-specific env thread-safely
env = parse_env
Gitlab::Git::HookEnv.set(gl_repository, env) if container
@@ -101,6 +110,7 @@ module API
response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR)
end
end
+ # rubocop: enable Metrics/AbcSize
def send_git_audit_streaming_event(msg)
# Defined in EE
diff --git a/lib/api/internal/error_tracking.rb b/lib/api/internal/error_tracking.rb
index bad790b0e43..1680ac8afb5 100644
--- a/lib/api/internal/error_tracking.rb
+++ b/lib/api/internal/error_tracking.rb
@@ -12,7 +12,7 @@ module API
input = params['error_tracking_token']
if headers.key?(GITLAB_ERROR_TRACKING_TOKEN_HEADER)
- input ||= Base64.decode64(headers[GITLAB_ERROR_TRACKING_TOKEN_HEADER])
+ input ||= headers[GITLAB_ERROR_TRACKING_TOKEN_HEADER]
end
input&.chomp!
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index f7c6e48e54f..6f964d5636b 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -4,6 +4,8 @@ module API
# Kubernetes Internal API
module Internal
class Kubernetes < ::API::Base
+ include Gitlab::Utils::StrongMemoize
+
feature_category :kubernetes_management
before do
check_feature_enabled
@@ -54,6 +56,27 @@ module API
::Clusters::AgentTokens::TrackUsageService.new(agent_token).execute
end
+
+ def agent_has_access_to_project?(project)
+ Guest.can?(:download_code, project) || agent.has_access_to?(project)
+ end
+
+ def count_events
+ strong_memoize(:count_events) do
+ events = params.slice(:gitops_sync_count, :k8s_api_proxy_request_count)
+ events.transform_keys! { |event| event.to_s.chomp('_count') }
+ events = params[:counters]&.slice(:gitops_sync, :k8s_api_proxy_request) unless events.present?
+ events
+ end
+ end
+
+ def increment_unique_events
+ events = params[:unique_counters]&.slice(:agent_users_using_ci_tunnel)
+
+ events&.each do |event, entity_ids|
+ increment_unique_values(event, entity_ids)
+ end
+ end
end
namespace 'internal' do
@@ -79,6 +102,24 @@ module API
default_branch: project.default_branch_or_main
}
end
+
+ desc 'Gets project info' do
+ detail 'Retrieves project info (if authorized)'
+ end
+ route_setting :authentication, cluster_agent_token_allowed: true
+ get '/project_info', urgency: :low do
+ project = find_project(params[:id])
+
+ not_found! unless agent_has_access_to_project?(project)
+
+ status 200
+ {
+ project_id: project.id,
+ gitaly_info: gitaly_info(project),
+ gitaly_repository: gitaly_repository(project),
+ default_branch: project.default_branch_or_main
+ }
+ end
end
namespace 'kubernetes/agent_configuration', urgency: :low do
@@ -103,14 +144,27 @@ module API
detail 'Updates usage metrics for agent'
end
params do
+ # Todo: Remove gitops_sync_count and k8s_api_proxy_request_count in the next milestone
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/369489
+ # We're only keeping it for backwards compatibility until KAS is released
+ # using `counts:` instead
optional :gitops_sync_count, type: Integer, desc: 'The count to increment the gitops_sync metric by'
optional :k8s_api_proxy_request_count, type: Integer, desc: 'The count to increment the k8s_api_proxy_request_count metric by'
+ optional :counters, type: Hash do
+ optional :gitops_sync, type: Integer, desc: 'The count to increment the gitops_sync metric by'
+ optional :k8s_api_proxy_request, type: Integer, desc: 'The count to increment the k8s_api_proxy_request_count metric by'
+ end
+ mutually_exclusive :counters, :gitops_sync_count
+ mutually_exclusive :counters, :k8s_api_proxy_request_count
+
+ optional :unique_counters, type: Hash do
+ optional :agent_users_using_ci_tunnel, type: Set[Integer], desc: 'A set of user ids that have interacted a CI Tunnel to'
+ end
end
post '/' do
- events = params.slice(:gitops_sync_count, :k8s_api_proxy_request_count)
- events.transform_keys! { |event| event.to_s.chomp('_count') }
+ Gitlab::UsageDataCounters::KubernetesAgentCounter.increment_event_counts(count_events) if count_events
- Gitlab::UsageDataCounters::KubernetesAgentCounter.increment_event_counts(events)
+ increment_unique_events
no_content!
rescue ArgumentError => e
diff --git a/lib/api/issue_links.rb b/lib/api/issue_links.rb
index c07c2c1994e..563fb3358ed 100644
--- a/lib/api/issue_links.rb
+++ b/lib/api/issue_links.rb
@@ -37,7 +37,7 @@ module API
requires :target_project_id, type: String, desc: 'The ID of the target project'
requires :target_issue_iid, type: Integer, desc: 'The IID of the target issue'
optional :link_type, type: String, values: IssueLink.link_types.keys,
- desc: 'The type of the relation'
+ desc: 'The type of the relation'
end
# rubocop: disable CodeReuse/ActiveRecord
post ':id/issues/:issue_iid/links' do
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 971163c18db..b6ad34424a6 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -16,7 +16,7 @@ module API
optional :labels, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
optional :milestone, type: String, desc: 'Milestone title'
optional :milestone_id, types: String, values: %w[Any None Upcoming Started],
- desc: 'Return issues assigned to milestones without the specified timebox value ("Any", "None", "Upcoming" or "Started")'
+ desc: 'Return issues assigned to milestones without the specified timebox value ("Any", "None", "Upcoming" or "Started")'
mutually_exclusive :milestone_id, :milestone
optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IID array of issues'
@@ -27,8 +27,8 @@ module API
optional :assignee_id, type: Integer, desc: 'Return issues which are not assigned to the user with the given ID'
optional :assignee_username, type: Array[String], check_assignees_count: true,
- coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
- desc: 'Return issues which are not assigned to the user with the given username'
+ coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
+ desc: 'Return issues which are not assigned to the user with the given username'
mutually_exclusive :assignee_id, :assignee_username
use :negatable_issue_filter_params_ee
@@ -40,7 +40,7 @@ module API
# 'milestone_id' only accepts wildcard values 'Any', 'None', 'Upcoming', 'Started'
# the param has '_id' in the name to keep consistency (ex. assignee_id accepts id and wildcard values).
optional :milestone_id, types: String, values: %w[Any None Upcoming Started],
- desc: 'Return issues assigned to milestones with the specified timebox value ("Any", "None", "Upcoming" or "Started")'
+ desc: 'Return issues assigned to milestones with the specified timebox value ("Any", "None", "Upcoming" or "Started")'
optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IID array of issues'
optional :search, type: String, desc: 'Search issues for text present in the title, description, or any combination of these'
optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
@@ -51,10 +51,10 @@ module API
mutually_exclusive :author_id, :author_username
optional :assignee_id, types: [Integer, String], integer_none_any: true,
- desc: 'Return issues which are assigned to the user with the given ID'
+ desc: 'Return issues which are assigned to the user with the given ID'
optional :assignee_username, type: Array[String], check_assignees_count: true,
- coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
- desc: 'Return issues which are assigned to the user with the given username'
+ coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
+ desc: 'Return issues which are assigned to the user with the given username'
mutually_exclusive :assignee_id, :assignee_username
optional :created_after, type: DateTime, desc: 'Return issues created after the specified time'
@@ -77,13 +77,13 @@ module API
params :issues_params do
optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
optional :state, type: String, values: %w[opened closed all], default: 'all',
- desc: 'Return opened, closed, or all issues'
+ desc: 'Return opened, closed, or all issues'
optional :order_by, type: String, values: Helpers::IssuesHelpers.sort_options, default: 'created_at',
- desc: 'Return issues ordered by `created_at`, `due_date`, `label_priority`, `milestone_due`, `popularity`, `priority`, `relative_position`, `title`, or `updated_at` fields.'
+ desc: 'Return issues ordered by `created_at`, `due_date`, `label_priority`, `milestone_due`, `popularity`, `priority`, `relative_position`, `title`, or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
- desc: 'Return issues sorted in `asc` or `desc` order.'
+ desc: 'Return issues sorted in `asc` or `desc` order.'
optional :due_date, type: String, values: %w[0 any today tomorrow overdue week month next_month_and_previous_two_weeks] << '',
- desc: 'Return issues that have no due date (`0`), or whose due date is this week, this month, between two weeks ago and next month, or which are overdue. Accepts: `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`, `0`'
+ desc: 'Return issues that have no due date (`0`), or whose due date is this week, this month, between two weeks ago and next month, or which are overdue. Accepts: `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`, `0`'
optional :issue_type, type: String, values: WorkItems::Type.allowed_types_for_issues, desc: "The type of the issue. Accepts: #{WorkItems::Type.allowed_types_for_issues.join(', ')}"
use :issues_stats_params
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index e2d4f5d823a..0a107a96d61 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -23,11 +23,11 @@ module API
end
params do
optional :with_counts, type: Boolean, default: false,
- desc: 'Include issue and merge request counts'
+ desc: 'Include issue and merge request counts'
optional :include_ancestor_groups, type: Boolean, default: true,
- desc: 'Include ancestor groups'
+ desc: 'Include ancestor groups'
optional :search, type: String,
- desc: 'Keyword to filter labels by. This feature was added in GitLab 13.6'
+ desc: 'Keyword to filter labels by. This feature was added in GitLab 13.6'
use :pagination
end
get ':id/labels' do
@@ -40,7 +40,7 @@ module API
end
params do
optional :include_ancestor_groups, type: Boolean, default: true,
- desc: 'Include ancestor groups'
+ desc: 'Include ancestor groups'
end
get ':id/labels/:name' do
get_label(user_project, Entities::ProjectLabel, declared_params)
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index c465087c4a2..1f8255fd6a4 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -2,7 +2,9 @@
module API
class Markdown < ::API::Base
- feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
+ before { authenticate! if Feature.enabled?(:authenticate_markdown_api, type: :ops) }
+
+ feature_category :team_planning
params do
requires :text, type: String, desc: "The markdown text to render"
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index e2481dcb8c1..fb0221ee907 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -43,6 +43,9 @@ module API
end
end
+ # The sha verification done by the maven api is between:
+ # - the sha256 set by workhorse helpers
+ # - the sha256 of the sha1 of the uploaded package file
def verify_package_file(package_file, uploaded_file)
stored_sha256 = Digest::SHA256.hexdigest(package_file.file_sha1)
expected_sha256 = uploaded_file.sha256
@@ -50,6 +53,16 @@ module API
if stored_sha256 == expected_sha256
no_content!
else
+ # Track sha1 conflicts.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/367356
+ Gitlab::ErrorTracking.log_exception(
+ ArgumentError.new,
+ message: 'maven package file sha1 conflict',
+ stored_sha1: package_file.file_sha1,
+ received_sha256: uploaded_file.sha256,
+ sha256_hexdigest_of_stored_sha1: stored_sha256
+ )
+
conflict!
end
end
@@ -270,12 +283,12 @@ module API
''
else
file_params = {
- file: params[:file],
- size: params['file.size'],
+ file: params[:file],
+ size: params['file.size'],
file_name: file_name,
file_type: params['file.type'],
file_sha1: params['file.sha1'],
- file_md5: params['file.md5']
+ file_md5: params['file.md5']
}
::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job)).execute
diff --git a/lib/api/members.rb b/lib/api/members.rb
index b94f68f60b5..d26fdd09ee7 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -91,7 +91,7 @@ module API
authorize_read_source_member!(source_type, source)
- members = find_all_members(source)
+ members = find_all_members(source).order(access_level: :desc)
member = members.find_by!(user_id: params[:user_id])
present_members member
@@ -156,9 +156,9 @@ module API
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
optional :skip_subresources, type: Boolean, default: false,
- desc: 'Flag indicating if the deletion of direct memberships of the removed member in subgroups and projects should be skipped'
+ desc: 'Flag indicating if the deletion of direct memberships of the removed member in subgroups and projects should be skipped'
optional :unassign_issuables, type: Boolean, default: false,
- desc: 'Flag indicating if the removed member should be unassigned from any issues or merge requests within given group or project'
+ desc: 'Flag indicating if the removed member should be unassigned from any issues or merge requests within given group or project'
end
# rubocop: disable CodeReuse/ActiveRecord
delete ":id/members/:user_id", feature_category: feature_category do
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 156a92802b0..a8f58e91067 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -121,6 +121,10 @@ module API
merge_request.permits_force_push?
end
+ def recheck_mergeability_of(merge_requests:)
+ merge_requests.each { |mr| mr.check_mergeability(async: true) }
+ end
+
params :merge_requests_params do
use :merge_requests_base_params
use :optional_merge_requests_search_params
@@ -155,7 +159,7 @@ module API
params do
use :merge_requests_params
optional :non_archived, type: Boolean, desc: 'Return merge requests from non archived projects',
- default: true
+ default: true
end
get ":id/merge_requests", feature_category: :code_review, urgency: :low do
validate_anonymous_search_access! if declared_params[:search].present?
@@ -206,7 +210,9 @@ module API
options = serializer_options_for(merge_requests).merge(project: user_project)
options[:project] = user_project
- present_cached merge_requests, expires_in: 2.days, **options
+ recheck_mergeability_of(merge_requests: merge_requests) unless options[:skip_merge_status_recheck]
+
+ present_cached merge_requests, expires_in: 8.hours, cache_context: -> (mr) { "#{current_user&.cache_key}:#{mr.merge_status}" }, **options
end
desc 'Create a merge request' do
@@ -283,6 +289,17 @@ module API
present paginate(participants), with: Entities::UserBasic
end
+ desc 'Get the reviewers of a merge request' do
+ success Entities::MergeRequestReviewer
+ end
+ get ':id/merge_requests/:merge_request_iid/reviewers', feature_category: :code_review, urgency: :low do
+ merge_request = find_merge_request_with_access(params[:merge_request_iid])
+
+ reviewers = ::Kaminari.paginate_array(merge_request.merge_request_reviewers)
+
+ present paginate(reviewers), with: Entities::MergeRequestReviewer
+ end
+
desc 'Get the commits of a merge request' do
success Entities::Commit
end
@@ -455,11 +472,7 @@ module API
not_allowed! if !immediately_mergeable && !automatically_mergeable
- if Feature.enabled?(:change_response_code_merge_status, user_project)
- render_api_error!('Branch cannot be merged', 422) unless merge_request.mergeable?(skip_ci_check: automatically_mergeable)
- else
- render_api_error!('Branch cannot be merged', 406) unless merge_request.mergeable?(skip_ci_check: automatically_mergeable)
- end
+ render_api_error!('Branch cannot be merged', 422) unless merge_request.mergeable?(skip_ci_check: automatically_mergeable)
check_sha_param!(params, merge_request)
@@ -481,7 +494,11 @@ module API
.execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
end
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ if immediately_mergeable && !merge_request.merged?
+ render_api_error!("Branch cannot be merged", 422)
+ else
+ present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ end
end
desc 'Returns the up to date merge-ref HEAD commit'
diff --git a/lib/api/metrics/dashboard/annotations.rb b/lib/api/metrics/dashboard/annotations.rb
index 6fc90da87d4..478adcdce70 100644
--- a/lib/api/metrics/dashboard/annotations.rb
+++ b/lib/api/metrics/dashboard/annotations.rb
@@ -20,11 +20,11 @@ module API
resource annotations_source[:resource] do
params do
requires :starting_at, type: DateTime,
- desc: 'Date time indicating starting moment to which the annotation relates.'
+ desc: 'Date time indicating starting moment to which the annotation relates.'
optional :ending_at, type: DateTime,
- desc: 'Date time indicating ending moment to which the annotation relates.'
+ desc: 'Date time indicating ending moment to which the annotation relates.'
requires :dashboard_path, type: String, coerce_with: -> (val) { CGI.unescape(val) },
- desc: 'The path to a file defining the dashboard on which the annotation should be added'
+ desc: 'The path to a file defining the dashboard on which the annotation should be added'
requires :description, type: String, desc: 'The description of the annotation'
end
diff --git a/lib/api/metrics/user_starred_dashboards.rb b/lib/api/metrics/user_starred_dashboards.rb
index 83d95f8b062..4d5396acccb 100644
--- a/lib/api/metrics/user_starred_dashboards.rb
+++ b/lib/api/metrics/user_starred_dashboards.rb
@@ -13,7 +13,7 @@ module API
params do
requires :dashboard_path, type: String, allow_blank: false, coerce_with: ->(val) { CGI.unescape(val) },
- desc: 'Url encoded path to a file defining the dashboard to which the star should be added'
+ desc: 'Url encoded path to a file defining the dashboard to which the star should be added'
end
post ':id/metrics/user_starred_dashboards' do
@@ -30,7 +30,7 @@ module API
params do
optional :dashboard_path, type: String, allow_blank: false, coerce_with: ->(val) { CGI.unescape(val) },
- desc: 'Url encoded path to a file defining the dashboard from which the star should be removed'
+ desc: 'Url encoded path to a file defining the dashboard from which the star should be removed'
end
delete ':id/metrics/user_starred_dashboards' do
diff --git a/lib/api/milestone_responses.rb b/lib/api/milestone_responses.rb
index d75ed3a48d7..2fd3239b44a 100644
--- a/lib/api/milestone_responses.rb
+++ b/lib/api/milestone_responses.rb
@@ -14,12 +14,12 @@ module API
params :list_params do
optional :state, type: String, values: %w[active closed all], default: 'all',
- desc: 'Return "active", "closed", or "all" milestones'
+ desc: 'Return "active", "closed", or "all" milestones'
optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IIDs of the milestones'
optional :title, type: String, desc: 'The title of the milestones'
optional :search, type: String, desc: 'The search criteria for the title or description of the milestone'
optional :include_parent_milestones, type: Grape::API::Boolean, default: false,
- desc: 'Include group milestones from parent and its ancestors'
+ desc: 'Include group milestones from parent and its ancestors'
use :pagination
end
@@ -27,7 +27,7 @@ module API
requires :milestone_id, type: Integer, desc: 'The milestone ID number'
optional :title, type: String, desc: 'The title of the milestone'
optional :state_event, type: String, values: %w[close activate],
- desc: 'The state event of the milestone '
+ desc: 'The state event of the milestone '
use :optional_params
at_least_one_of :title, :description, :start_date, :due_date, :state_event
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 2a854bd785e..77c479c529a 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -30,7 +30,7 @@ module API
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return notes sorted in `asc` or `desc` order.'
optional :activity_filter, type: String, values: UserPreference::NOTES_FILTERS.stringify_keys.keys, default: 'all_notes',
- desc: 'The type of notables which are returned.'
+ desc: 'The type of notables which are returned.'
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -73,7 +73,8 @@ module API
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
requires :body, type: String, desc: 'The content of a note'
- optional :confidential, type: Boolean, desc: 'Confidentiality note flag, default is false'
+ optional :confidential, type: Boolean, desc: '[Deprecated in 15.3] Renamed to internal'
+ optional :internal, type: Boolean, desc: 'Internal note flag, default is false'
optional :created_at, type: String, desc: 'The creation date of the note'
optional :merge_request_diff_head_sha, type: String, desc: 'The SHA of the head commit'
end
@@ -87,7 +88,7 @@ module API
note: params[:body],
noteable_type: noteables_str.classify,
noteable_id: noteable.id,
- confidential: params[:confidential],
+ internal: params[:internal] || params[:confidential],
created_at: params[:created_at],
merge_request_diff_head_sha: params[:merge_request_diff_head_sha]
}
diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb
index 2e7f8475509..34d3a5150da 100644
--- a/lib/api/pages_domains.rb
+++ b/lib/api/pages_domains.rb
@@ -97,7 +97,7 @@ module API
optional :certificate, types: [File, String], desc: 'The certificate', as: :user_provided_certificate
optional :key, types: [File, String], desc: 'The key', as: :user_provided_key
optional :auto_ssl_enabled, allow_blank: false, type: Boolean, default: false,
- desc: "Enables automatic generation of SSL certificates issued by Let's Encrypt for custom domains."
+ desc: "Enables automatic generation of SSL certificates issued by Let's Encrypt for custom domains."
# rubocop:enable Scalability/FileUploads
all_or_none_of :user_provided_certificate, :user_provided_key
end
@@ -123,7 +123,7 @@ module API
optional :certificate, types: [File, String], desc: 'The certificate', as: :user_provided_certificate
optional :key, types: [File, String], desc: 'The key', as: :user_provided_key
optional :auto_ssl_enabled, allow_blank: true, type: Boolean,
- desc: "Enables automatic generation of SSL certificates issued by Let's Encrypt for custom domains."
+ desc: "Enables automatic generation of SSL certificates issued by Let's Encrypt for custom domains."
# rubocop:enable Scalability/FileUploads
end
put ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do
diff --git a/lib/api/pagination_params.rb b/lib/api/pagination_params.rb
index 85ac50d5bec..bdb69d0ba44 100644
--- a/lib/api/pagination_params.rb
+++ b/lib/api/pagination_params.rb
@@ -20,6 +20,26 @@ module API
optional :page, type: Integer, default: 1, desc: 'Current page number'
optional :per_page, type: Integer, default: 20, desc: 'Number of items per page', except_values: [0]
end
+
+ def verify_pagination_params!
+ return if Feature.disabled?(:only_positive_pagination_values)
+
+ page = begin
+ Integer(params[:page])
+ rescue ArgumentError, TypeError
+ nil
+ end
+
+ return render_structured_api_error!({ error: 'page does not have a valid value' }, 400) if page&.< 1
+
+ per_page = begin
+ Integer(params[:per_page])
+ rescue ArgumentError, TypeError
+ nil
+ end
+
+ return render_structured_api_error!({ error: 'per_page does not have a valid value' }, 400) if per_page&.< 1
+ end
end
end
end
diff --git a/lib/api/personal_access_tokens.rb b/lib/api/personal_access_tokens.rb
index f8b744bb14b..0d7d2dc6a0c 100644
--- a/lib/api/personal_access_tokens.rb
+++ b/lib/api/personal_access_tokens.rb
@@ -57,9 +57,14 @@ module API
get ':id' do
token = PersonalAccessToken.find_by_id(params[:id])
- unauthorized! unless token && Ability.allowed?(current_user, :read_user_personal_access_tokens, token.user)
-
- present token, with: Entities::PersonalAccessToken
+ allowed = Ability.allowed?(current_user, :read_user_personal_access_tokens, token&.user)
+
+ if allowed
+ present token, with: Entities::PersonalAccessToken
+ else
+ # Only admins should be informed if the token doesn't exist
+ current_user.admin? ? not_found! : unauthorized!
+ end
end
delete 'self' do
diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb
index 79a5ca531e1..800966408fc 100644
--- a/lib/api/project_packages.rb
+++ b/lib/api/project_packages.rb
@@ -32,10 +32,11 @@ module API
optional :package_name, type: String,
desc: 'Return packages with this name'
optional :include_versionless, type: Boolean,
- desc: 'Returns packages without a version'
+ desc: 'Returns packages without a version'
optional :status, type: String, values: Packages::Package.statuses.keys,
- desc: 'Return packages with specified status'
+ desc: 'Return packages with specified status'
end
+ route_setting :authentication, job_token_allowed: true
get ':id/packages' do
packages = ::Packages::PackagesFinder.new(
user_project,
@@ -52,6 +53,7 @@ module API
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
end
+ route_setting :authentication, job_token_allowed: true
get ':id/packages/:package_id' do
package = ::Packages::PackageFinder
.new(user_project, params[:package_id]).execute
@@ -65,6 +67,7 @@ module API
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
end
+ route_setting :authentication, job_token_allowed: true
delete ':id/packages/:package_id' do
authorize_destroy_package!(user_project)
diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb
index fe0e837c596..f6e1286d616 100644
--- a/lib/api/project_templates.rb
+++ b/lib/api/project_templates.rb
@@ -37,7 +37,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the template'
optional :source_template_project_id, type: Integer,
- desc: 'The project id where a given template is being stored. This is useful when multiple templates from different projects have the same name'
+ desc: 'The project id where a given template is being stored. This is useful when multiple templates from different projects have the same name'
optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses'
optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses'
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6530887c1c3..6ed480518ee 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -688,11 +688,11 @@ module API
optional :search, type: String, desc: 'Return list of groups matching the search criteria'
optional :skip_groups, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of group ids to exclude from list'
optional :with_shared, type: Boolean, default: false,
- desc: 'Include shared groups'
+ desc: 'Include shared groups'
optional :shared_visible_only, type: Boolean, default: false,
- desc: 'Limit to shared groups user has access to'
+ desc: 'Limit to shared groups user has access to'
optional :shared_min_access_level, type: Integer, values: Gitlab::Access.all_values,
- desc: 'Limit returned shared groups by minimum access level to the project'
+ desc: 'Limit returned shared groups by minimum access level to the project'
use :pagination
end
get ':id/groups', feature_category: :source_code_management do
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index a4f5dfefae6..38bafac25b2 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -61,8 +61,8 @@ module API
values: ProtectedBranch::MergeAccessLevel.allowed_access_levels,
desc: 'Access levels allowed to merge (defaults: `40`, maintainer access level)'
optional :allow_force_push, type: Boolean,
- default: false,
- desc: 'Allow force push for all users with push access.'
+ default: false,
+ desc: 'Allow force push for all users with push access.'
use :optional_params_ee
end
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index aecd6f9eef8..10e879ec70b 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -23,9 +23,9 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of the group to get releases for'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
- desc: 'Return projects sorted in ascending and descending order by released_at'
+ desc: 'Return projects sorted in ascending and descending order by released_at'
optional :simple, type: Boolean, default: false,
- desc: 'Return only the ID, URL, name, and path of each project'
+ desc: 'Return only the ID, URL, name, and path of each project'
use :pagination
end
@@ -61,7 +61,7 @@ module API
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return releases sorted in `asc` or `desc` order.'
optional :include_html_description, type: Boolean,
- desc: 'If `true`, a response includes HTML rendered markdown of the release description.'
+ 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
@@ -89,7 +89,7 @@ module API
params do
requires :tag_name, type: String, desc: 'The name of the tag', as: :tag
optional :include_html_description, type: Boolean,
- desc: 'If `true`, a response includes HTML rendered markdown of the release description.'
+ 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
@@ -186,6 +186,8 @@ module API
.execute
if result[:status] == :success
+ log_release_deleted_audit_event
+
present result[:release], with: Entities::Release, current_user: current_user
else
render_api_error!(result[:message], result[:http_status])
@@ -238,6 +240,10 @@ module API
# extended in EE
end
+ def log_release_deleted_audit_event
+ # extended in EE
+ end
+
def log_release_milestones_updated_audit_event
# extended in EE
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 4c7cc6be8b6..cef72d898e6 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -99,11 +99,17 @@ module API
optional :recursive, type: Boolean, default: false, desc: 'Used to get a recursive tree'
use :pagination
- optional :pagination, type: String, values: %w(legacy keyset), default: 'legacy', desc: 'Specify the pagination method'
+ optional :pagination, type: String, values: %w(legacy keyset none), default: 'legacy', desc: 'Specify the pagination method ("none" is only valid if "recursive" is true)'
- given pagination: -> (value) { value == 'keyset' } do
+ given pagination: ->(value) { value == 'keyset' } do
optional :page_token, type: String, desc: 'Record from which to start the keyset pagination'
end
+
+ given pagination: ->(value) { value == 'none' } do
+ given recursive: ->(value) { value == false } do
+ validates([:pagination], except_values: { value: 'none', message: 'cannot be "none" unless "recursive" is true' })
+ end
+ end
end
get ':id/repository/tree', urgency: :low do
tree_finder = ::Repositories::TreeFinder.new(user_project, declared_params(include_missing: false))
diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb
index e6c54faebd9..85bbd0879b7 100644
--- a/lib/api/rubygem_packages.rb
+++ b/lib/api/rubygem_packages.rb
@@ -109,7 +109,7 @@ module API
).execute(:rubygems, name: ::Packages::Rubygems::TEMPORARY_PACKAGE_NAME)
file_params = {
- file: params[:file],
+ file: params[:file],
file_name: PACKAGE_FILENAME
}
diff --git a/lib/api/search.rb b/lib/api/search.rb
index fd4d46cf77d..c78aff705ab 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -59,11 +59,15 @@ module API
end
def search(additional_params = {})
- results = search_service(additional_params).search_objects(preload_method)
+ @search_duration_s = Benchmark.realtime do
+ @results = search_service(additional_params).search_objects(preload_method)
+ end
+
+ set_global_search_log_information
Gitlab::UsageDataCounters::SearchCounter.count(:all_searches)
- paginate(results)
+ paginate(@results)
end
def snippets?
@@ -83,6 +87,23 @@ module API
# Defining this method here as a noop allows us to easily extend it in
# EE, without having to modify this file directly.
end
+
+ def search_type
+ 'basic'
+ end
+
+ def search_scope
+ params[:scope]
+ end
+
+ def set_global_search_log_information
+ Gitlab::Instrumentation::GlobalSearchApi.set_information(
+ type: search_type,
+ level: search_service.level,
+ scope: search_scope,
+ search_duration_s: @search_duration_s
+ )
+ end
end
resource :search do
diff --git a/lib/api/sidekiq_metrics.rb b/lib/api/sidekiq_metrics.rb
index bca1376d489..e279e63181d 100644
--- a/lib/api/sidekiq_metrics.rb
+++ b/lib/api/sidekiq_metrics.rb
@@ -22,14 +22,14 @@ module API
def process_metrics
Sidekiq::ProcessSet.new(false).map do |process|
{
- hostname: process['hostname'],
- pid: process['pid'],
- tag: process['tag'],
- started_at: Time.at(process['started_at']),
- queues: process['queues'],
- labels: process['labels'],
+ hostname: process['hostname'],
+ pid: process['pid'],
+ tag: process['tag'],
+ started_at: Time.at(process['started_at']),
+ queues: process['queues'],
+ labels: process['labels'],
concurrency: process['concurrency'],
- busy: process['busy']
+ busy: process['busy']
}
end
end
diff --git a/lib/api/support/git_access_actor.rb b/lib/api/support/git_access_actor.rb
index 71395086ac2..f450630afdd 100644
--- a/lib/api/support/git_access_actor.rb
+++ b/lib/api/support/git_access_actor.rb
@@ -32,6 +32,10 @@ module API
key || user
end
+ def deploy_key_or_user
+ key.instance_of?(DeployKey) ? key : user
+ end
+
def username
user&.username
end
diff --git a/lib/api/topics.rb b/lib/api/topics.rb
index 15f79e75be3..a08b4c6c107 100644
--- a/lib/api/topics.rb
+++ b/lib/api/topics.rb
@@ -12,6 +12,7 @@ module API
end
params do
optional :search, type: String, desc: 'Return list of topics matching the search criteria'
+ optional :without_projects, type: Boolean, desc: 'Return list of topics without assigned projects'
use :pagination
end
get 'topics' do
diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb
index 2d528ad47a2..1fbd7cf5afc 100644
--- a/lib/api/unleash.rb
+++ b/lib/api/unleash.rb
@@ -33,8 +33,10 @@ module API
end
end
+ # We decrease the urgency of this endpoint until the maxmemory issue of redis-cache has been resolved.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/365575#note_1033611872 for more information.
desc 'Get a list of features'
- get 'client/features' do
+ get 'client/features', urgency: :low do
if ::Feature.enabled?(:cache_unleash_client_api, project)
present_feature_flags
else
diff --git a/lib/api/user_counts.rb b/lib/api/user_counts.rb
index d0b1e458a27..388aa5e375c 100644
--- a/lib/api/user_counts.rb
+++ b/lib/api/user_counts.rb
@@ -12,19 +12,13 @@ module API
get do
unauthorized! unless current_user
- counts = {
+ {
merge_requests: current_user.assigned_open_merge_requests_count, # @deprecated
assigned_issues: current_user.assigned_open_issues_count,
assigned_merge_requests: current_user.assigned_open_merge_requests_count,
review_requested_merge_requests: current_user.review_requested_open_merge_requests_count,
todos: current_user.todos_pending_count
}
-
- if current_user&.mr_attention_requests_enabled?
- counts[:attention_requests] = current_user.attention_requested_open_merge_requests_count
- end
-
- counts
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index d66d86a9055..c93c0f601a0 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -68,9 +68,9 @@ module API
params :sort_params do
optional :order_by, type: String, values: %w[id name username created_at updated_at],
- default: 'id', desc: 'Return users ordered by a field'
+ default: 'id', desc: 'Return users ordered by a field'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
- desc: 'Return users sorted in ascending and descending order'
+ desc: 'Return users sorted in ascending and descending order'
end
end
@@ -940,7 +940,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the personal access token'
requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, values: ::Gitlab::Auth.all_available_scopes.map(&:to_s),
- desc: 'The array of scopes of the personal access token'
+ desc: 'The array of scopes of the personal access token'
optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
end
post feature_category: :authentication_and_authorization do