summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/admin/ci/variables.rb2
-rw-r--r--lib/api/admin/plan_limits.rb1
-rw-r--r--lib/api/api.rb3
-rw-r--r--lib/api/bulk_imports.rb91
-rw-r--r--lib/api/ci/runner.rb18
-rw-r--r--lib/api/ci/runners.rb34
-rw-r--r--lib/api/commit_statuses.rb60
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb41
-rw-r--r--lib/api/debian_group_packages.rb6
-rw-r--r--lib/api/entities/basic_project_details.rb2
-rw-r--r--lib/api/entities/bulk_import.rb13
-rw-r--r--lib/api/entities/bulk_imports/entity.rb22
-rw-r--r--lib/api/entities/bulk_imports/entity_failure.rb15
-rw-r--r--lib/api/entities/ci/job.rb2
-rw-r--r--lib/api/entities/ci/job_request/artifacts.rb20
-rw-r--r--lib/api/entities/ci/job_request/cache.rb13
-rw-r--r--lib/api/entities/ci/job_request/credentials.rb13
-rw-r--r--lib/api/entities/ci/job_request/dependency.rb14
-rw-r--r--lib/api/entities/ci/job_request/git_info.rb16
-rw-r--r--lib/api/entities/ci/job_request/image.rb14
-rw-r--r--lib/api/entities/ci/job_request/job_info.rb14
-rw-r--r--lib/api/entities/ci/job_request/port.rb13
-rw-r--r--lib/api/entities/ci/job_request/response.rb39
-rw-r--r--lib/api/entities/ci/job_request/runner_info.rb14
-rw-r--r--lib/api/entities/ci/job_request/service.rb13
-rw-r--r--lib/api/entities/ci/job_request/step.rb13
-rw-r--r--lib/api/entities/ci/runner.rb19
-rw-r--r--lib/api/entities/ci/runner_details.rb36
-rw-r--r--lib/api/entities/ci/runner_registration_details.rb11
-rw-r--r--lib/api/entities/group_detail.rb1
-rw-r--r--lib/api/entities/helm/index.rb14
-rw-r--r--lib/api/entities/job_request/artifacts.rb18
-rw-r--r--lib/api/entities/job_request/cache.rb11
-rw-r--r--lib/api/entities/job_request/credentials.rb11
-rw-r--r--lib/api/entities/job_request/dependency.rb12
-rw-r--r--lib/api/entities/job_request/git_info.rb14
-rw-r--r--lib/api/entities/job_request/image.rb12
-rw-r--r--lib/api/entities/job_request/job_info.rb12
-rw-r--r--lib/api/entities/job_request/port.rb11
-rw-r--r--lib/api/entities/job_request/response.rb37
-rw-r--r--lib/api/entities/job_request/runner_info.rb12
-rw-r--r--lib/api/entities/job_request/service.rb11
-rw-r--r--lib/api/entities/job_request/step.rb11
-rw-r--r--lib/api/entities/label.rb4
-rw-r--r--lib/api/entities/plan_limit.rb1
-rw-r--r--lib/api/entities/project.rb8
-rw-r--r--lib/api/entities/project_integration.rb25
-rw-r--r--lib/api/entities/project_integration_basic.rb (renamed from lib/api/entities/project_service_basic.rb)6
-rw-r--r--lib/api/entities/project_service.rb17
-rw-r--r--lib/api/entities/resource_access_token.rb11
-rw-r--r--lib/api/entities/resource_access_token_with_token.rb9
-rw-r--r--lib/api/entities/runner.rb17
-rw-r--r--lib/api/entities/runner_details.rb34
-rw-r--r--lib/api/entities/runner_registration_details.rb9
-rw-r--r--lib/api/entities/user.rb2
-rw-r--r--lib/api/error_tracking_collector.rb73
-rw-r--r--lib/api/geo.rb29
-rw-r--r--lib/api/group_avatar.rb22
-rw-r--r--lib/api/groups.rb25
-rw-r--r--lib/api/helm_packages.rb79
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/helpers/caching.rb107
-rw-r--r--lib/api/helpers/groups_helpers.rb4
-rw-r--r--lib/api/helpers/integrations_helpers.rb (renamed from lib/api/helpers/services_helpers.rb)16
-rw-r--r--lib/api/helpers/projects_helpers.rb2
-rw-r--r--lib/api/helpers/runner.rb4
-rw-r--r--lib/api/helpers/snippets_helpers.rb18
-rw-r--r--lib/api/internal/base.rb11
-rw-r--r--lib/api/internal/kubernetes.rb2
-rw-r--r--lib/api/issues.rb10
-rw-r--r--lib/api/job_artifacts.rb2
-rw-r--r--lib/api/lint.rb6
-rw-r--r--lib/api/nuget_project_packages.rb120
-rw-r--r--lib/api/project_packages.rb4
-rw-r--r--lib/api/project_snippets.rb6
-rw-r--r--lib/api/projects.rb6
-rw-r--r--lib/api/releases.rb4
-rw-r--r--lib/api/repositories.rb34
-rw-r--r--lib/api/resource_access_tokens.rb8
-rw-r--r--lib/api/services.rb97
-rw-r--r--lib/api/settings.rb4
-rw-r--r--lib/api/snippets.rb6
-rw-r--r--lib/api/system_hooks.rb1
-rw-r--r--lib/api/usage_data.rb2
-rw-r--r--lib/api/usage_data_non_sql_metrics.rb2
-rw-r--r--lib/api/usage_data_queries.rb2
-rw-r--r--lib/api/variables.rb2
87 files changed, 1034 insertions, 580 deletions
diff --git a/lib/api/admin/ci/variables.rb b/lib/api/admin/ci/variables.rb
index 654d3a48162..0462878c90c 100644
--- a/lib/api/admin/ci/variables.rb
+++ b/lib/api/admin/ci/variables.rb
@@ -8,7 +8,7 @@ module API
before { authenticated_as_admin! }
- feature_category :continuous_integration
+ feature_category :pipeline_authoring
namespace 'admin' do
namespace 'ci' do
diff --git a/lib/api/admin/plan_limits.rb b/lib/api/admin/plan_limits.rb
index 92f7d3dce0d..ab6a4e4a04a 100644
--- a/lib/api/admin/plan_limits.rb
+++ b/lib/api/admin/plan_limits.rb
@@ -41,6 +41,7 @@ module API
optional :npm_max_file_size, type: Integer, desc: 'Maximum NPM package file size in bytes'
optional :nuget_max_file_size, type: Integer, desc: 'Maximum NuGet package file size in bytes'
optional :pypi_max_file_size, type: Integer, desc: 'Maximum PyPI package file size in bytes'
+ optional :terraform_module_max_file_size, type: Integer, desc: 'Maximum Terraform Module package file size in bytes'
end
put "application/plan_limits" do
params = declared_params(include_missing: false)
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 2a3033753f7..f9e89191a36 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -152,6 +152,7 @@ module API
mount ::API::Boards
mount ::API::Branches
mount ::API::BroadcastMessages
+ mount ::API::BulkImports
mount ::API::Ci::Pipelines
mount ::API::Ci::PipelineSchedules
mount ::API::Ci::Runner
@@ -166,12 +167,14 @@ module API
mount ::API::Deployments
mount ::API::Environments
mount ::API::ErrorTracking
+ mount ::API::ErrorTrackingCollector
mount ::API::Events
mount ::API::FeatureFlags
mount ::API::FeatureFlagsUserLists
mount ::API::Features
mount ::API::Files
mount ::API::FreezePeriods
+ mount ::API::Geo
mount ::API::GroupAvatar
mount ::API::GroupBoards
mount ::API::GroupClusters
diff --git a/lib/api/bulk_imports.rb b/lib/api/bulk_imports.rb
new file mode 100644
index 00000000000..189851cee65
--- /dev/null
+++ b/lib/api/bulk_imports.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module API
+ class BulkImports < ::API::Base
+ include PaginationParams
+
+ feature_category :importers
+
+ helpers do
+ def bulk_imports
+ @bulk_imports ||= ::BulkImports::ImportsFinder.new(user: current_user, status: params[:status]).execute
+ end
+
+ def bulk_import
+ @bulk_import ||= bulk_imports.find(params[:import_id])
+ end
+
+ def bulk_import_entities
+ @bulk_import_entities ||= ::BulkImports::EntitiesFinder.new(user: current_user, bulk_import: bulk_import, status: params[:status]).execute
+ end
+
+ def bulk_import_entity
+ @bulk_import_entity ||= bulk_import_entities.find(params[:entity_id])
+ end
+ end
+
+ before { authenticate! }
+
+ resource :bulk_imports do
+ desc 'List all GitLab Migrations' do
+ detail 'This feature was introduced in GitLab 14.1.'
+ end
+ params do
+ use :pagination
+ optional :status, type: String, values: BulkImport.all_human_statuses,
+ desc: 'Return GitLab Migrations with specified status'
+ end
+ get do
+ present paginate(bulk_imports), with: Entities::BulkImport
+ end
+
+ desc "List all GitLab Migrations' entities" do
+ detail 'This feature was introduced in GitLab 14.1.'
+ end
+ params do
+ use :pagination
+ optional :status, type: String, values: ::BulkImports::Entity.all_human_statuses,
+ desc: "Return all GitLab Migrations' entities with specified status"
+ end
+ get :entities do
+ entities = ::BulkImports::EntitiesFinder.new(user: current_user, status: params[:status]).execute
+
+ present paginate(entities), with: Entities::BulkImports::Entity
+ end
+
+ desc 'Get GitLab Migration details' do
+ detail 'This feature was introduced in GitLab 14.1.'
+ end
+ params do
+ requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
+ end
+ get ':import_id' do
+ present bulk_import, with: Entities::BulkImport
+ end
+
+ desc "List GitLab Migration entities" do
+ detail 'This feature was introduced in GitLab 14.1.'
+ end
+ 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'
+ use :pagination
+ end
+ get ':import_id/entities' do
+ present paginate(bulk_import_entities), with: Entities::BulkImports::Entity
+ end
+
+ desc 'Get GitLab Migration entity details' do
+ detail 'This feature was introduced in GitLab 14.1.'
+ end
+ params do
+ requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
+ requires :entity_id, type: Integer, desc: "The ID of GitLab Migration entity"
+ end
+ get ':import_id/entities/:entity_id' do
+ present bulk_import_entity, with: Entities::BulkImports::Entity
+ end
+ end
+ end
+end
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index c4e0b699524..0bac6fe2054 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -7,11 +7,11 @@ module API
content_type :txt, 'text/plain'
- feature_category :continuous_integration
+ feature_category :runner
resource :runners do
desc 'Registers a new Runner' do
- success Entities::RunnerRegistrationDetails
+ success Entities::Ci::RunnerRegistrationDetails
http_codes [[201, 'Runner was created'], [403, 'Forbidden']]
end
params do
@@ -34,10 +34,10 @@ module API
if runner_registration_token_valid?
# Create shared runner. Requires admin access
attributes.merge(runner_type: :instance_type)
- elsif @project = Project.find_by_runners_token(params[:token])
+ elsif runner_registrar_valid?('project') && @project = Project.find_by_runners_token(params[:token])
# Create a specific runner for the project
attributes.merge(runner_type: :project_type, projects: [@project])
- elsif @group = Group.find_by_runners_token(params[:token])
+ elsif runner_registrar_valid?('group') && @group = Group.find_by_runners_token(params[:token])
# Create a specific runner for the group
attributes.merge(runner_type: :group_type, groups: [@group])
else
@@ -47,7 +47,7 @@ module API
@runner = ::Ci::Runner.create(attributes)
if @runner.persisted?
- present @runner, with: Entities::RunnerRegistrationDetails
+ present @runner, with: Entities::Ci::RunnerRegistrationDetails
else
render_validation_error!(@runner)
end
@@ -82,7 +82,7 @@ module API
before { set_application_context }
desc 'Request a job' do
- success Entities::JobRequest::Response
+ success Entities::Ci::JobRequest::Response
http_codes [[201, 'Job was scheduled'],
[204, 'No job for Runner'],
[403, 'Forbidden']]
@@ -214,6 +214,10 @@ module API
.new(job, content_range: content_range)
.execute(request.body.read)
+ if result.status == 403
+ break error!('403 Forbidden', 403)
+ end
+
if result.status == 416
break error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{result.stream_size}" })
end
@@ -263,7 +267,7 @@ module API
end
desc 'Upload artifacts for job' do
- success Entities::JobRequest::Response
+ success Entities::Ci::JobRequest::Response
http_codes [[201, 'Artifact uploaded'],
[400, 'Bad request'],
[403, 'Forbidden'],
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 44ffc941cfa..7f755b1a4d4 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -7,11 +7,11 @@ module API
before { authenticate! }
- feature_category :continuous_integration
+ feature_category :runner
resource :runners do
desc 'Get runners available for user' do
- success Entities::Runner
+ success Entities::Ci::Runner
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
@@ -30,11 +30,11 @@ module API
runners = filter_runners(runners, params[:status], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
- present paginate(runners), with: Entities::Runner
+ present paginate(runners), with: Entities::Ci::Runner
end
desc 'Get all runners - shared and specific' do
- success Entities::Runner
+ success Entities::Ci::Runner
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
@@ -55,11 +55,11 @@ module API
runners = filter_runners(runners, params[:status], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
- present paginate(runners), with: Entities::Runner
+ present paginate(runners), with: Entities::Ci::Runner
end
desc "Get runner's details" do
- success Entities::RunnerDetails
+ success Entities::Ci::RunnerDetails
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
@@ -68,11 +68,11 @@ module API
runner = get_runner(params[:id])
authenticate_show_runner!(runner)
- present runner, with: Entities::RunnerDetails, current_user: current_user
+ present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
end
desc "Update runner's details" do
- success Entities::RunnerDetails
+ success Entities::Ci::RunnerDetails
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
@@ -92,14 +92,14 @@ module API
update_service = ::Ci::UpdateRunnerService.new(runner)
if update_service.update(declared_params(include_missing: false))
- present runner, with: Entities::RunnerDetails, current_user: current_user
+ present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
else
render_validation_error!(runner)
end
end
desc 'Remove a runner' do
- success Entities::Runner
+ success Entities::Ci::Runner
end
params do
requires :id, type: Integer, desc: 'The ID of the runner'
@@ -139,7 +139,7 @@ module API
before { authorize_admin_project }
desc 'Get runners available for project' do
- success Entities::Runner
+ success Entities::Ci::Runner
end
params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
@@ -158,11 +158,11 @@ module API
runners = filter_runners(runners, params[:scope])
runners = apply_filter(runners, params)
- present paginate(runners), with: Entities::Runner
+ present paginate(runners), with: Entities::Ci::Runner
end
desc 'Enable a runner for a project' do
- success Entities::Runner
+ success Entities::Ci::Runner
end
params do
requires :runner_id, type: Integer, desc: 'The ID of the runner'
@@ -172,14 +172,14 @@ module API
authenticate_enable_runner!(runner)
if runner.assign_to(user_project)
- present runner, with: Entities::Runner
+ present runner, with: Entities::Ci::Runner
else
render_validation_error!(runner)
end
end
desc "Disable project's runner" do
- success Entities::Runner
+ success Entities::Ci::Runner
end
params do
requires :runner_id, type: Integer, desc: 'The ID of the runner'
@@ -204,7 +204,7 @@ module API
before { authorize_admin_group }
desc 'Get runners available for group' do
- success Entities::Runner
+ success Entities::Ci::Runner
end
params do
optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
@@ -218,7 +218,7 @@ module API
runners = ::Ci::Runner.belonging_to_group(user_group.id, include_ancestors: true)
runners = apply_filter(runners, params)
- present paginate(runners), with: Entities::Runner
+ present paginate(runners), with: Entities::Ci::Runner
end
end
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 27fee7fdea2..1785362656e 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -99,40 +99,26 @@ module API
updatable_optional_attributes = %w[target_url description coverage]
status.assign_attributes(attributes_for_keys(updatable_optional_attributes))
- if status.valid?
- status.update_older_statuses_retried! if Feature.enabled?(:ci_fix_commit_status_retried, user_project, default_enabled: :yaml)
- else
- render_validation_error!(status)
- end
+ render_validation_error!(status) unless status.valid?
- begin
- case params[:state]
- when 'pending'
- status.enqueue!
- when 'running'
- status.enqueue
- status.run!
- when 'success'
- status.success!
- when 'failed'
- status.drop!(:api_failure)
- when 'canceled'
- status.cancel!
- else
- render_api_error!('invalid state', 400)
- end
+ response = ::Ci::Pipelines::AddJobService.new(pipeline).execute!(status) do |job|
+ apply_job_state!(job)
+ rescue ::StateMachines::InvalidTransition => e
+ render_api_error!(e.message, 400)
+ end
- if pipeline.latest?
- MergeRequest.where(source_project: user_project, source_branch: ref)
- .update_all(head_pipeline_id: pipeline.id)
- end
+ render_validation_error!(response.payload[:job]) unless response.success?
- present status, with: Entities::CommitStatus
- rescue StateMachines::InvalidTransition => e
- render_api_error!(e.message, 400)
+ if pipeline.latest?
+ MergeRequest
+ .where(source_project: user_project, source_branch: ref)
+ .update_all(head_pipeline_id: pipeline.id)
end
+
+ present response.payload[:job], with: Entities::CommitStatus
end
# rubocop: enable CodeReuse/ActiveRecord
+
helpers do
def commit
strong_memoize(:commit) do
@@ -146,6 +132,24 @@ module API
pipelines = pipelines.for_id(params[:pipeline_id]) if params[:pipeline_id]
pipelines
end
+
+ def apply_job_state!(job)
+ case params[:state]
+ when 'pending'
+ job.enqueue!
+ when 'running'
+ job.enqueue
+ job.run!
+ when 'success'
+ job.success!
+ when 'failed'
+ job.drop!(:api_failure)
+ when 'canceled'
+ job.cancel!
+ else
+ render_api_error!('invalid state', 400)
+ end
+ end
end
end
end
diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index c79ae3068b4..7740ba6bfa6 100644
--- a/lib/api/concerns/packages/debian_package_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -6,20 +6,17 @@ module API
module DebianPackageEndpoints
extend ActiveSupport::Concern
- DISTRIBUTION_REGEX = %r{[a-zA-Z0-9][a-zA-Z0-9.-]*}.freeze
- COMPONENT_REGEX = %r{[a-z-]+}.freeze
- ARCHITECTURE_REGEX = %r{[a-z][a-z0-9]*}.freeze
LETTER_REGEX = %r{(lib)?[a-z0-9]}.freeze
PACKAGE_REGEX = API::NO_SLASH_URL_PART_REGEX
DISTRIBUTION_REQUIREMENTS = {
- distribution: DISTRIBUTION_REGEX
+ distribution: ::Packages::Debian::DISTRIBUTION_REGEX
}.freeze
COMPONENT_ARCHITECTURE_REQUIREMENTS = {
- component: COMPONENT_REGEX,
- architecture: ARCHITECTURE_REGEX
+ component: ::Packages::Debian::COMPONENT_REGEX,
+ architecture: ::Packages::Debian::ARCHITECTURE_REGEX
}.freeze
COMPONENT_LETTER_SOURCE_PACKAGE_REQUIREMENTS = {
- component: COMPONENT_REGEX,
+ component: ::Packages::Debian::COMPONENT_REGEX,
letter: LETTER_REGEX,
source_package: PACKAGE_REGEX
}.freeze
@@ -40,6 +37,14 @@ module API
.sent_through(:http_basic_auth)
end
+ helpers do
+ def present_release_file
+ distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename_or_suite: params[:distribution]).execute.last!
+
+ present_carrierwave_file!(distribution.file)
+ end
+ end
+
format :txt
content_type :txt, 'text/plain'
@@ -65,8 +70,7 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'Release' do
- # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
- 'TODO Release'
+ present_release_file
end
# GET {projects|groups}/:id/packages/debian/dists/*distribution/InRelease
@@ -76,7 +80,8 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'InRelease' do
- not_found!
+ # Signature to be added in 7.3 of https://gitlab.com/groups/gitlab-org/-/epics/6057#note_582697034
+ present_release_file
end
params do
@@ -92,8 +97,20 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'Packages' do
- # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
- 'TODO Packages'
+ relation = "::Packages::Debian::#{project_or_group.class.name}ComponentFile".constantize
+
+ component_file = relation
+ .preload_distribution
+ .with_container(project_or_group)
+ .with_codename_or_suite(params[:distribution])
+ .with_component_name(params[:component])
+ .with_file_type(:packages)
+ .with_architecture_name(params[:architecture])
+ .with_compression_type(nil)
+ .order_created_asc
+ .last!
+
+ present_carrierwave_file!(component_file.file)
end
end
end
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index c6116a8b28f..191ed42a5b8 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -24,6 +24,12 @@ module API
end
namespace ':id/-' do
+ helpers do
+ def project_or_group
+ user_group
+ end
+ end
+
include ::API::Concerns::Packages::DebianPackageEndpoints
end
end
diff --git a/lib/api/entities/basic_project_details.rb b/lib/api/entities/basic_project_details.rb
index c75b74b4368..0b231906ccd 100644
--- a/lib/api/entities/basic_project_details.rb
+++ b/lib/api/entities/basic_project_details.rb
@@ -6,7 +6,7 @@ module API
include ::API::ProjectsRelationBuilder
include Gitlab::Utils::StrongMemoize
- expose :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
+ expose :default_branch_or_main, as: :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
# Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770
expose :topic_names, as: :tag_list
diff --git a/lib/api/entities/bulk_import.rb b/lib/api/entities/bulk_import.rb
new file mode 100644
index 00000000000..373ae486dcf
--- /dev/null
+++ b/lib/api/entities/bulk_import.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class BulkImport < Grape::Entity
+ expose :id
+ expose :status_name, as: :status
+ expose :source_type
+ expose :created_at
+ expose :updated_at
+ end
+ end
+end
diff --git a/lib/api/entities/bulk_imports/entity.rb b/lib/api/entities/bulk_imports/entity.rb
new file mode 100644
index 00000000000..e8c31256b17
--- /dev/null
+++ b/lib/api/entities/bulk_imports/entity.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module BulkImports
+ class Entity < Grape::Entity
+ expose :id
+ expose :bulk_import_id
+ expose :status_name, as: :status
+ expose :source_full_path
+ expose :destination_name
+ expose :destination_namespace
+ expose :parent_id
+ expose :namespace_id
+ expose :project_id
+ expose :created_at
+ expose :updated_at
+ expose :failures, using: EntityFailure
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/bulk_imports/entity_failure.rb b/lib/api/entities/bulk_imports/entity_failure.rb
new file mode 100644
index 00000000000..a3dbe3280ee
--- /dev/null
+++ b/lib/api/entities/bulk_imports/entity_failure.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module BulkImports
+ class EntityFailure < Grape::Entity
+ expose :pipeline_class
+ expose :pipeline_step
+ expose :exception_class
+ expose :correlation_id_value
+ expose :created_at
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job.rb b/lib/api/entities/ci/job.rb
index 76487ed01dc..cf87684ce55 100644
--- a/lib/api/entities/ci/job.rb
+++ b/lib/api/entities/ci/job.rb
@@ -7,7 +7,7 @@ module API
# artifacts_file is included in job_artifacts, but kept for backward compatibility (remove in api/v5)
expose :artifacts_file, using: ::API::Entities::Ci::JobArtifactFile, if: -> (job, opts) { job.artifacts? }
expose :job_artifacts, as: :artifacts, using: ::API::Entities::Ci::JobArtifact
- expose :runner, with: ::API::Entities::Runner
+ expose :runner, with: ::API::Entities::Ci::Runner
expose :artifacts_expire_at
expose :tag_list do |job|
job.tags.map(&:name).sort
diff --git a/lib/api/entities/ci/job_request/artifacts.rb b/lib/api/entities/ci/job_request/artifacts.rb
new file mode 100644
index 00000000000..4b09db40504
--- /dev/null
+++ b/lib/api/entities/ci/job_request/artifacts.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Artifacts < Grape::Entity
+ expose :name
+ expose :untracked
+ expose :paths
+ expose :exclude, expose_nil: false
+ expose :when
+ expose :expire_in
+ expose :artifact_type
+ expose :artifact_format
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/cache.rb b/lib/api/entities/ci/job_request/cache.rb
new file mode 100644
index 00000000000..9820719b4f0
--- /dev/null
+++ b/lib/api/entities/ci/job_request/cache.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Cache < Grape::Entity
+ expose :key, :untracked, :paths, :policy, :when
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/credentials.rb b/lib/api/entities/ci/job_request/credentials.rb
new file mode 100644
index 00000000000..57cdd9c9b19
--- /dev/null
+++ b/lib/api/entities/ci/job_request/credentials.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Credentials < Grape::Entity
+ expose :type, :url, :username, :password
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/dependency.rb b/lib/api/entities/ci/job_request/dependency.rb
new file mode 100644
index 00000000000..2c6ed417714
--- /dev/null
+++ b/lib/api/entities/ci/job_request/dependency.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Dependency < Grape::Entity
+ expose :id, :name, :token
+ expose :artifacts_file, using: Entities::Ci::JobArtifactFile, if: ->(job, _) { job.artifacts? }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/git_info.rb b/lib/api/entities/ci/job_request/git_info.rb
new file mode 100644
index 00000000000..872c896b870
--- /dev/null
+++ b/lib/api/entities/ci/job_request/git_info.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class GitInfo < Grape::Entity
+ expose :repo_url, :ref, :sha, :before_sha
+ expose :ref_type
+ expose :refspecs
+ expose :git_depth, as: :depth
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/image.rb b/lib/api/entities/ci/job_request/image.rb
new file mode 100644
index 00000000000..8e404a8fa02
--- /dev/null
+++ b/lib/api/entities/ci/job_request/image.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Image < Grape::Entity
+ expose :name, :entrypoint
+ expose :ports, using: Entities::Ci::JobRequest::Port
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/job_info.rb b/lib/api/entities/ci/job_request/job_info.rb
new file mode 100644
index 00000000000..5c3f4b08af2
--- /dev/null
+++ b/lib/api/entities/ci/job_request/job_info.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class JobInfo < Grape::Entity
+ expose :id, :name, :stage
+ expose :project_id, :project_name
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/port.rb b/lib/api/entities/ci/job_request/port.rb
new file mode 100644
index 00000000000..31aa06ff843
--- /dev/null
+++ b/lib/api/entities/ci/job_request/port.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Port < Grape::Entity
+ expose :number, :protocol, :name
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/response.rb b/lib/api/entities/ci/job_request/response.rb
new file mode 100644
index 00000000000..86c945cb236
--- /dev/null
+++ b/lib/api/entities/ci/job_request/response.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Response < Grape::Entity
+ expose :id
+ expose :token
+ expose :allow_git_fetch
+
+ expose :job_info, using: Entities::Ci::JobRequest::JobInfo do |model|
+ model
+ end
+
+ expose :git_info, using: Entities::Ci::JobRequest::GitInfo do |model|
+ model
+ end
+
+ expose :runner_info, using: Entities::Ci::JobRequest::RunnerInfo do |model|
+ model
+ end
+
+ expose :runner_variables, as: :variables
+ expose :steps, using: Entities::Ci::JobRequest::Step
+ expose :image, using: Entities::Ci::JobRequest::Image
+ expose :services, using: Entities::Ci::JobRequest::Service
+ expose :artifacts, using: Entities::Ci::JobRequest::Artifacts
+ expose :cache, using: Entities::Ci::JobRequest::Cache
+ expose :credentials, using: Entities::Ci::JobRequest::Credentials
+ expose :all_dependencies, as: :dependencies, using: Entities::Ci::JobRequest::Dependency
+ expose :features
+ end
+ end
+ end
+ end
+end
+
+API::Entities::Ci::JobRequest::Response.prepend_mod_with('API::Entities::Ci::JobRequest::Response')
diff --git a/lib/api/entities/ci/job_request/runner_info.rb b/lib/api/entities/ci/job_request/runner_info.rb
new file mode 100644
index 00000000000..96336a1080e
--- /dev/null
+++ b/lib/api/entities/ci/job_request/runner_info.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class RunnerInfo < Grape::Entity
+ expose :metadata_timeout, as: :timeout
+ expose :runner_session_url
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/service.rb b/lib/api/entities/ci/job_request/service.rb
new file mode 100644
index 00000000000..f89b95c1d5c
--- /dev/null
+++ b/lib/api/entities/ci/job_request/service.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Service < Entities::Ci::JobRequest::Image
+ expose :alias, :command
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/step.rb b/lib/api/entities/ci/job_request/step.rb
new file mode 100644
index 00000000000..2a0c4cd032e
--- /dev/null
+++ b/lib/api/entities/ci/job_request/step.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Step < Grape::Entity
+ expose :name, :script, :timeout, :when, :allow_failure
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/runner.rb b/lib/api/entities/ci/runner.rb
new file mode 100644
index 00000000000..ede698696de
--- /dev/null
+++ b/lib/api/entities/ci/runner.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ class Runner < Grape::Entity
+ expose :id
+ expose :description
+ expose :ip_address
+ expose :active
+ expose :instance_type?, as: :is_shared
+ expose :runner_type
+ expose :name
+ expose :online?, as: :online
+ expose :status
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/runner_details.rb b/lib/api/entities/ci/runner_details.rb
new file mode 100644
index 00000000000..9d44da7e5b3
--- /dev/null
+++ b/lib/api/entities/ci/runner_details.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ class RunnerDetails < Runner
+ expose :tag_list
+ expose :run_untagged
+ expose :locked
+ expose :maximum_timeout
+ expose :access_level
+ expose :version, :revision, :platform, :architecture
+ expose :contacted_at
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ expose :projects, with: Entities::BasicProjectDetails do |runner, options|
+ if options[:current_user].admin? # rubocop: disable Cop/UserAdmin
+ runner.projects
+ else
+ options[:current_user].authorized_projects.where(id: runner.projects)
+ 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
+ else
+ options[:current_user].authorized_groups.where(id: runner.groups)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/runner_registration_details.rb b/lib/api/entities/ci/runner_registration_details.rb
new file mode 100644
index 00000000000..fa7e44c9e40
--- /dev/null
+++ b/lib/api/entities/ci/runner_registration_details.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ class RunnerRegistrationDetails < Grape::Entity
+ expose :id, :token
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/group_detail.rb b/lib/api/entities/group_detail.rb
index 408254a89be..61f35d0f784 100644
--- a/lib/api/entities/group_detail.rb
+++ b/lib/api/entities/group_detail.rb
@@ -7,6 +7,7 @@ module API
SharedGroupWithGroup.represent(group.shared_with_group_links.public_or_visible_to_user(group, options[:current_user]))
end
expose :runners_token, if: lambda { |group, options| options[:user_can_admin_group] }
+ expose :prevent_sharing_groups_outside_hierarchy, if: ->(group) { group.root? }
expose :projects, using: Entities::Project do |group, options|
projects = GroupProjectsFinder.new(
diff --git a/lib/api/entities/helm/index.rb b/lib/api/entities/helm/index.rb
new file mode 100644
index 00000000000..168298f24b6
--- /dev/null
+++ b/lib/api/entities/helm/index.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Helm
+ class Index < Grape::Entity
+ expose :api_version, as: :apiVersion
+ expose :entries
+ expose :generated
+ expose :server_info, as: :serverInfo
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/job_request/artifacts.rb b/lib/api/entities/job_request/artifacts.rb
deleted file mode 100644
index 0d27f5a9189..00000000000
--- a/lib/api/entities/job_request/artifacts.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Artifacts < Grape::Entity
- expose :name
- expose :untracked
- expose :paths
- expose :exclude, expose_nil: false
- expose :when
- expose :expire_in
- expose :artifact_type
- expose :artifact_format
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/cache.rb b/lib/api/entities/job_request/cache.rb
deleted file mode 100644
index cd533d7e5b3..00000000000
--- a/lib/api/entities/job_request/cache.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Cache < Grape::Entity
- expose :key, :untracked, :paths, :policy, :when
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/credentials.rb b/lib/api/entities/job_request/credentials.rb
deleted file mode 100644
index cdac5566cbd..00000000000
--- a/lib/api/entities/job_request/credentials.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Credentials < Grape::Entity
- expose :type, :url, :username, :password
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/dependency.rb b/lib/api/entities/job_request/dependency.rb
deleted file mode 100644
index 7d6ec832ba1..00000000000
--- a/lib/api/entities/job_request/dependency.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Dependency < Grape::Entity
- expose :id, :name, :token
- expose :artifacts_file, using: Entities::Ci::JobArtifactFile, if: ->(job, _) { job.artifacts? }
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/git_info.rb b/lib/api/entities/job_request/git_info.rb
deleted file mode 100644
index e07099263b5..00000000000
--- a/lib/api/entities/job_request/git_info.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class GitInfo < Grape::Entity
- expose :repo_url, :ref, :sha, :before_sha
- expose :ref_type
- expose :refspecs
- expose :git_depth, as: :depth
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/image.rb b/lib/api/entities/job_request/image.rb
deleted file mode 100644
index 47f4542d2d5..00000000000
--- a/lib/api/entities/job_request/image.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Image < Grape::Entity
- expose :name, :entrypoint
- expose :ports, using: Entities::JobRequest::Port
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/job_info.rb b/lib/api/entities/job_request/job_info.rb
deleted file mode 100644
index a4bcc9726d0..00000000000
--- a/lib/api/entities/job_request/job_info.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class JobInfo < Grape::Entity
- expose :id, :name, :stage
- expose :project_id, :project_name
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/port.rb b/lib/api/entities/job_request/port.rb
deleted file mode 100644
index ee427da8657..00000000000
--- a/lib/api/entities/job_request/port.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Port < Grape::Entity
- expose :number, :protocol, :name
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/response.rb b/lib/api/entities/job_request/response.rb
deleted file mode 100644
index 2e8dfc5bde0..00000000000
--- a/lib/api/entities/job_request/response.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Response < Grape::Entity
- expose :id
- expose :token
- expose :allow_git_fetch
-
- expose :job_info, using: Entities::JobRequest::JobInfo do |model|
- model
- end
-
- expose :git_info, using: Entities::JobRequest::GitInfo do |model|
- model
- end
-
- expose :runner_info, using: Entities::JobRequest::RunnerInfo do |model|
- model
- end
-
- expose :runner_variables, as: :variables
- expose :steps, using: Entities::JobRequest::Step
- expose :image, using: Entities::JobRequest::Image
- expose :services, using: Entities::JobRequest::Service
- expose :artifacts, using: Entities::JobRequest::Artifacts
- expose :cache, using: Entities::JobRequest::Cache
- expose :credentials, using: Entities::JobRequest::Credentials
- expose :all_dependencies, as: :dependencies, using: Entities::JobRequest::Dependency
- expose :features
- end
- end
- end
-end
-
-API::Entities::JobRequest::Response.prepend_mod_with('API::Entities::JobRequest::Response')
diff --git a/lib/api/entities/job_request/runner_info.rb b/lib/api/entities/job_request/runner_info.rb
deleted file mode 100644
index e6d2e8d9e85..00000000000
--- a/lib/api/entities/job_request/runner_info.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class RunnerInfo < Grape::Entity
- expose :metadata_timeout, as: :timeout
- expose :runner_session_url
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/service.rb b/lib/api/entities/job_request/service.rb
deleted file mode 100644
index 9ad5abf4e9e..00000000000
--- a/lib/api/entities/job_request/service.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Service < Entities::JobRequest::Image
- expose :alias, :command
- end
- end
- end
-end
diff --git a/lib/api/entities/job_request/step.rb b/lib/api/entities/job_request/step.rb
deleted file mode 100644
index 498dd017fb4..00000000000
--- a/lib/api/entities/job_request/step.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- module JobRequest
- class Step < Grape::Entity
- expose :name, :script, :timeout, :when, :allow_failure
- end
- end
- end
-end
diff --git a/lib/api/entities/label.rb b/lib/api/entities/label.rb
index ca9a0912331..dc147f33671 100644
--- a/lib/api/entities/label.rb
+++ b/lib/api/entities/label.rb
@@ -18,7 +18,9 @@ module API
end
expose :subscribed do |label, options|
- label.subscribed?(options[:current_user], options[:parent])
+ label.subscribed?(options[:current_user]) || (
+ options[:parent].is_a?(::Project) && label.subscribed?(options[:current_user], options[:parent])
+ )
end
end
end
diff --git a/lib/api/entities/plan_limit.rb b/lib/api/entities/plan_limit.rb
index 40e8b348c18..04ec44b5167 100644
--- a/lib/api/entities/plan_limit.rb
+++ b/lib/api/entities/plan_limit.rb
@@ -9,6 +9,7 @@ module API
expose :npm_max_file_size
expose :nuget_max_file_size
expose :pypi_max_file_size
+ expose :terraform_module_max_file_size
end
end
end
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 68d91fc6970..f5f565e5b07 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -53,13 +53,7 @@ module API
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
- expose(:container_registry_enabled) do |project, options|
- if ::Feature.enabled?(:read_container_registry_access_level, project.namespace, default_enabled: :yaml)
- project.feature_available?(:container_registry, options[:current_user])
- else
- project.read_attribute(:container_registry_enabled)
- end
- end
+ expose(:container_registry_enabled) { |project, options| project.feature_available?(:container_registry, options[:current_user]) }
expose :service_desk_enabled
expose :service_desk_address
diff --git a/lib/api/entities/project_integration.rb b/lib/api/entities/project_integration.rb
new file mode 100644
index 00000000000..649e4d015b8
--- /dev/null
+++ b/lib/api/entities/project_integration.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class ProjectIntegration < Entities::ProjectIntegrationBasic
+ # Expose serialized properties
+ expose :properties do |integration, options|
+ # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
+
+ attributes =
+ if integration.data_fields_present?
+ integration.data_fields.as_json.keys
+ else
+ integration.properties.keys
+ end
+
+ attributes &= integration.api_field_names
+
+ attributes.each_with_object({}) do |attribute, hash|
+ hash[attribute] = integration.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/project_service_basic.rb b/lib/api/entities/project_integration_basic.rb
index eb97ca69a82..2870123b83d 100644
--- a/lib/api/entities/project_service_basic.rb
+++ b/lib/api/entities/project_integration_basic.rb
@@ -2,10 +2,10 @@
module API
module Entities
- class ProjectServiceBasic < Grape::Entity
+ class ProjectIntegrationBasic < Grape::Entity
expose :id, :title
- expose :slug do |service|
- service.to_param.dasherize
+ expose :slug do |integration|
+ integration.to_param.dasherize
end
expose :created_at, :updated_at, :active
expose :commit_events, :push_events, :issues_events, :confidential_issues_events
diff --git a/lib/api/entities/project_service.rb b/lib/api/entities/project_service.rb
deleted file mode 100644
index 947cec1e3cd..00000000000
--- a/lib/api/entities/project_service.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- class ProjectService < Entities::ProjectServiceBasic
- # Expose serialized properties
- expose :properties do |service, options|
- # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- if service.data_fields_present?
- service.data_fields.as_json.slice(*service.api_field_names)
- else
- service.properties.slice(*service.api_field_names)
- end
- end
- end
- end
-end
diff --git a/lib/api/entities/resource_access_token.rb b/lib/api/entities/resource_access_token.rb
new file mode 100644
index 00000000000..a1c7b28af45
--- /dev/null
+++ b/lib/api/entities/resource_access_token.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class ResourceAccessToken < Entities::PersonalAccessToken
+ expose :access_level do |token, options|
+ options[:project].project_member(token.user).access_level
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/resource_access_token_with_token.rb b/lib/api/entities/resource_access_token_with_token.rb
new file mode 100644
index 00000000000..edbd9b285de
--- /dev/null
+++ b/lib/api/entities/resource_access_token_with_token.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class ResourceAccessTokenWithToken < Entities::ResourceAccessToken
+ expose :token
+ end
+ end
+end
diff --git a/lib/api/entities/runner.rb b/lib/api/entities/runner.rb
deleted file mode 100644
index e78f14cf920..00000000000
--- a/lib/api/entities/runner.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- class Runner < Grape::Entity
- expose :id
- expose :description
- expose :ip_address
- expose :active
- expose :instance_type?, as: :is_shared
- expose :runner_type
- expose :name
- expose :online?, as: :online
- expose :status
- end
- end
-end
diff --git a/lib/api/entities/runner_details.rb b/lib/api/entities/runner_details.rb
deleted file mode 100644
index 0afe298ef64..00000000000
--- a/lib/api/entities/runner_details.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- class RunnerDetails < Runner
- expose :tag_list
- expose :run_untagged
- expose :locked
- expose :maximum_timeout
- expose :access_level
- expose :version, :revision, :platform, :architecture
- expose :contacted_at
-
- # rubocop: disable CodeReuse/ActiveRecord
- expose :projects, with: Entities::BasicProjectDetails do |runner, options|
- if options[:current_user].admin?
- runner.projects
- else
- options[:current_user].authorized_projects.where(id: runner.projects)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
- expose :groups, with: Entities::BasicGroupDetails do |runner, options|
- if options[:current_user].admin?
- runner.groups
- else
- options[:current_user].authorized_groups.where(id: runner.groups)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
- end
- end
-end
diff --git a/lib/api/entities/runner_registration_details.rb b/lib/api/entities/runner_registration_details.rb
deleted file mode 100644
index c8ed88ba10a..00000000000
--- a/lib/api/entities/runner_registration_details.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- class RunnerRegistrationDetails < Grape::Entity
- expose :id, :token
- end
- end
-end
diff --git a/lib/api/entities/user.rb b/lib/api/entities/user.rb
index 3ce6d03e236..973e80dd5ef 100644
--- a/lib/api/entities/user.rb
+++ b/lib/api/entities/user.rb
@@ -5,7 +5,7 @@ module API
class User < UserBasic
include UsersHelper
expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
- expose :bio, :bio_html, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title
+ expose :bio, :bio_html, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title, :pronouns
expose :bot?, as: :bot
expose :work_information do |user|
work_information(user)
diff --git a/lib/api/error_tracking_collector.rb b/lib/api/error_tracking_collector.rb
new file mode 100644
index 00000000000..08ff8d2e4d1
--- /dev/null
+++ b/lib/api/error_tracking_collector.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+module API
+ # This API is responsible for collecting error tracking information
+ # from sentry client. It allows us to use GitLab as an alternative to
+ # sentry backend. For more details see https://gitlab.com/gitlab-org/gitlab/-/issues/329596.
+ class ErrorTrackingCollector < ::API::Base
+ feature_category :error_tracking
+
+ content_type :envelope, 'application/x-sentry-envelope'
+ default_format :envelope
+
+ before do
+ not_found!('Project') unless project
+ not_found! unless feature_enabled?
+ end
+
+ helpers do
+ def project
+ @project ||= find_project(params[:id])
+ end
+
+ def feature_enabled?
+ ::Feature.enabled?(:integrated_error_tracking, project) &&
+ project.error_tracking_setting&.enabled?
+ end
+ end
+
+ desc 'Submit error tracking event to the project' do
+ detail 'This feature was introduced in GitLab 14.1.'
+ end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ post 'error_tracking/collector/api/:id/envelope' do
+ # There is a reason why we have such uncommon path.
+ # We depend on a client side error tracking software which
+ # modifies URL for its own reasons.
+ #
+ # When we give user a URL like this
+ # HOST/api/v4/error_tracking/collector/123
+ #
+ # Then error tracking software will convert it like this:
+ # HOST/api/v4/error_tracking/collector/api/123/envelope/
+
+ begin
+ parsed_request = ::ErrorTracking::Collector::SentryRequestParser.parse(request)
+ rescue StandardError
+ render_api_error!('Failed to parse sentry request', 400)
+ end
+
+ type = parsed_request[:request_type]
+
+ # Sentry sends 2 requests on each exception: transaction and event.
+ # Everything else is not a desired behavior.
+ unless type == 'transaction' || type == 'event'
+ render_api_error!('400 Bad Request', 400)
+
+ break
+ end
+
+ # We don't have use for transaction request yet,
+ # so we record only event one.
+ if type == 'event'
+ ::ErrorTracking::CollectErrorService
+ .new(project, nil, event: parsed_request[:event])
+ .execute
+ end
+
+ no_content!
+ end
+ end
+end
diff --git a/lib/api/geo.rb b/lib/api/geo.rb
new file mode 100644
index 00000000000..9fc610c9b32
--- /dev/null
+++ b/lib/api/geo.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module API
+ class Geo < ::API::Base
+ feature_category :geo_replication
+
+ helpers do
+ # Overridden in EE
+ def geo_proxy_response
+ {}
+ end
+ end
+
+ resource :geo do
+ # Workhorse calls this to determine if it is a Geo site that should proxy
+ # requests. Workhorse doesn't know if it's in a FOSS/EE context.
+ get '/proxy' do
+ require_gitlab_workhorse!
+
+ status :ok
+ content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
+
+ geo_proxy_response
+ end
+ end
+ end
+end
+
+API::Geo.prepend_mod
diff --git a/lib/api/group_avatar.rb b/lib/api/group_avatar.rb
index ddf6787f913..9063040c763 100644
--- a/lib/api/group_avatar.rb
+++ b/lib/api/group_avatar.rb
@@ -6,15 +6,27 @@ module API
feature_category :subgroups
- resource :groups do
+ params do
+ requires :id, type: String, desc: 'The ID of a group'
+ end
+ resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Download the group avatar' do
detail 'This feature was introduced in GitLab 14.0'
end
- params do
- requires :id, type: String, desc: 'The group id'
- end
get ':id/avatar' do
- present_carrierwave_file!(user_group.avatar)
+ avatar = user_group.avatar
+
+ not_found!('Avatar') if avatar.blank?
+
+ header(
+ 'Content-Disposition',
+ ActionDispatch::Http::ContentDisposition.format(
+ disposition: 'attachment',
+ filename: avatar.filename
+ )
+ )
+
+ present_carrierwave_file!(avatar)
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 0efb8b57885..9b6b28733ff 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -22,7 +22,7 @@ module API
optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
optional :search, type: String, desc: 'Search for a specific group'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
- optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
+ optional :order_by, type: String, values: %w[name path id similarity], default: 'name', desc: 'Order by name, path, id or similarity if searching'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
optional :top_level_only, type: Boolean, desc: 'Only include top level groups'
@@ -50,9 +50,8 @@ module API
groups = GroupsFinder.new(current_user, find_params).execute
groups = groups.search(params[:search], include_parents: true) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
- order_options = { params[:order_by] => params[:sort] }
- order_options["id"] ||= "asc"
- groups.reorder(order_options)
+
+ order_groups(groups)
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -123,6 +122,23 @@ module API
reorder_projects(projects)
end
+ def order_groups(groups)
+ return groups.sorted_by_similarity_and_parent_id_desc(params[:search]) if order_by_similarity?
+
+ groups.reorder(group_without_similarity_options) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def order_by_similarity?
+ params[:order_by] == 'similarity' && params[:search].present?
+ end
+
+ def group_without_similarity_options
+ order_options = { params[:order_by] => params[:sort] }
+ order_options['name'] = order_options.delete('similarity') if order_options.has_key?('similarity')
+ order_options["id"] ||= "asc"
+ order_options
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def handle_similarity_order(group, projects)
if params[:search].present? && Feature.enabled?(:similarity_search, group, default_enabled: true)
@@ -199,6 +215,7 @@ module API
optional :name, type: String, desc: 'The name of the group'
optional :path, type: String, desc: 'The path of the group'
use :optional_params
+ use :optional_update_params
use :optional_update_params_ee
end
put ':id' do
diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb
index dc5630a1395..4280744d8b4 100644
--- a/lib/api/helm_packages.rb
+++ b/lib/api/helm_packages.rb
@@ -10,11 +10,15 @@ module API
feature_category :package_registry
+ PACKAGE_FILENAME = 'package.tgz'
FILE_NAME_REQUIREMENTS = {
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
content_type :binary, 'application/octet-stream'
+ content_type :yaml, 'text/yaml'
+
+ formatter :yaml, -> (object, _) { object.serializable_hash.stringify_keys.to_yaml }
authenticate_with do |accept|
accept.token_types(:personal_access_token, :deploy_token, :job_token)
@@ -25,15 +29,33 @@ module API
require_packages_enabled!
end
- after_validation do
- not_found! unless Feature.enabled?(:helm_packages, authorized_user_project)
- end
-
params do
requires :id, type: String, desc: 'The ID or full path of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/helm' do
+ desc 'Download a chart index' do
+ detail 'This feature was introduced in GitLab 14.0'
+ end
+ params do
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ end
+
+ get ":channel/index.yaml" do
+ authorize_read_package!(authorized_user_project)
+
+ package_files = Packages::Helm::PackageFilesFinder.new(
+ authorized_user_project,
+ params[:channel],
+ order_by: 'created_at',
+ sort: 'desc'
+ ).execute
+
+ env['api.format'] = :yaml
+ present ::Packages::Helm::IndexPresenter.new(authorized_user_project, params[:id], package_files),
+ with: ::API::Entities::Helm::Index
+ end
+
desc 'Download a chart' do
detail 'This feature was introduced in GitLab 14.0'
end
@@ -50,6 +72,55 @@ module API
present_carrierwave_file!(package_file.file)
end
+
+ desc 'Authorize a chart upload from workhorse' do
+ detail 'This feature was introduced in GitLab 14.0'
+ end
+ params do
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ end
+ post "api/:channel/charts/authorize" do
+ authorize_workhorse!(
+ subject: authorized_user_project,
+ has_length: false,
+ maximum_size: authorized_user_project.actual_limits.helm_max_file_size
+ )
+ end
+
+ desc 'Upload a chart' do
+ detail 'This feature was introduced in GitLab 14.0'
+ end
+ params do
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ requires :chart, type: ::API::Validations::Types::WorkhorseFile, desc: 'The chart file to be published (generated by Multipart middleware)'
+ end
+ post "api/:channel/charts" do
+ authorize_upload!(authorized_user_project)
+ bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:helm_max_file_size, params[:chart].size)
+
+ package = ::Packages::CreateTemporaryPackageService.new(
+ authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job)
+ ).execute(:helm, name: ::Packages::Helm::TEMPORARY_PACKAGE_NAME)
+
+ chart_params = {
+ file: params[:chart],
+ file_name: PACKAGE_FILENAME
+ }
+
+ chart_package_file = ::Packages::CreatePackageFileService.new(
+ package, chart_params.merge(build: current_authenticated_job)
+ ).execute
+
+ track_package_event('push_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace)
+
+ ::Packages::Helm::ExtractionWorker.perform_async(params[:channel], chart_package_file.id) # rubocop:disable CodeReuse/Worker
+
+ created!
+ rescue ObjectStorage::RemoteStoreError => e
+ Gitlab::ErrorTracking.track_exception(e, extra: { channel: params[:channel], project_id: authorized_user_project.id })
+
+ forbidden!
+ end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 6ce04be373f..3398d5da7f5 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -577,10 +577,6 @@ module API
Gitlab::AppLogger.warn("Redis tracking event failed for event: #{event_name}, message: #{error.message}")
end
- def with_api_params(&block)
- yield({ api: true, request: request })
- end
-
protected
def project_finder_params_visibility_ce
diff --git a/lib/api/helpers/caching.rb b/lib/api/helpers/caching.rb
index f24ac7302c1..f567d85443f 100644
--- a/lib/api/helpers/caching.rb
+++ b/lib/api/helpers/caching.rb
@@ -8,18 +8,15 @@
module API
module Helpers
module Caching
- # @return [ActiveSupport::Duration]
- DEFAULT_EXPIRY = 1.day
-
+ include Gitlab::Cache::Helpers
# @return [Hash]
DEFAULT_CACHE_OPTIONS = {
- race_condition_ttl: 5.seconds
+ race_condition_ttl: 5.seconds,
+ version: 1
}.freeze
- # @return [ActiveSupport::Cache::Store]
- def cache
- Rails.cache
- end
+ # @return [Array]
+ PAGINATION_HEADERS = %w[X-Per-Page X-Page X-Next-Page X-Prev-Page Link X-Total X-Total-Pages].freeze
# This is functionally equivalent to the standard `#present` used in
# Grape endpoints, but the JSON for the object, or for each object of
@@ -45,7 +42,7 @@ module API
# @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry
# @param presenter_args [Hash] keyword arguments to be passed to the entity
# @return [Gitlab::Json::PrecompiledJson]
- def present_cached(obj_or_collection, with:, cache_context: -> (_) { current_user&.cache_key }, expires_in: DEFAULT_EXPIRY, **presenter_args)
+ def present_cached(obj_or_collection, with:, cache_context: -> (_) { current_user&.cache_key }, expires_in: Gitlab::Cache::Helpers::DEFAULT_EXPIRY, **presenter_args)
json =
if obj_or_collection.is_a?(Enumerable)
cached_collection(
@@ -79,15 +76,22 @@ module API
# @param key [Object] any object that can be converted into a cache key
# @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry
# @return [Gitlab::Json::PrecompiledJson]
- def cache_action(key, **cache_opts)
- json = cache.fetch(key, **apply_default_cache_options(cache_opts)) do
+ def cache_action(key, **custom_cache_opts)
+ cache_opts = apply_default_cache_options(custom_cache_opts)
+
+ json, cached_headers = cache.fetch(key, **cache_opts) do
response = yield
- if response.is_a?(Gitlab::Json::PrecompiledJson)
- response.to_s
- else
- Gitlab::Json.dump(response.as_json)
- end
+ cached_body = response.is_a?(Gitlab::Json::PrecompiledJson) ? response.to_s : Gitlab::Json.dump(response.as_json)
+ cached_headers = header.slice(*PAGINATION_HEADERS)
+
+ [cached_body, cached_headers]
+ end
+
+ cached_headers.each do |key, value|
+ next if header.key?(key)
+
+ header key, value
end
body Gitlab::Json::PrecompiledJson.new(json)
@@ -120,77 +124,6 @@ module API
def apply_default_cache_options(opts = {})
DEFAULT_CACHE_OPTIONS.merge(opts)
end
-
- # Optionally uses a `Proc` to add context to a cache key
- #
- # @param object [Object] must respond to #cache_key
- # @param context [Proc] a proc that will be called with the object as an argument, and which should return a
- # string or array of strings to be combined into the cache key
- # @return [String]
- def contextual_cache_key(object, context)
- return object.cache_key if context.nil?
-
- [object.cache_key, context.call(object)].flatten.join(":")
- end
-
- # Used for fetching or rendering a single object
- #
- # @param object [Object] the object to render
- # @param presenter [Grape::Entity]
- # @param presenter_args [Hash] keyword arguments to be passed to the entity
- # @param context [Proc]
- # @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry
- # @return [String]
- def cached_object(object, presenter:, presenter_args:, context:, expires_in:)
- cache.fetch(contextual_cache_key(object, context), expires_in: expires_in) do
- Gitlab::Json.dump(presenter.represent(object, **presenter_args).as_json)
- end
- end
-
- # Used for fetching or rendering multiple objects
- #
- # @param objects [Enumerable<Object>] the objects to render
- # @param presenter [Grape::Entity]
- # @param presenter_args [Hash] keyword arguments to be passed to the entity
- # @param context [Proc]
- # @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry
- # @return [Array<String>]
- def cached_collection(collection, presenter:, presenter_args:, context:, expires_in:)
- json = fetch_multi(collection, context: context, expires_in: expires_in) do |obj|
- Gitlab::Json.dump(presenter.represent(obj, **presenter_args).as_json)
- end
-
- json.values
- end
-
- # An adapted version of ActiveSupport::Cache::Store#fetch_multi.
- #
- # The original method only provides the missing key to the block,
- # not the missing object, so we have to create a map of cache keys
- # to the objects to allow us to pass the object to the missing value
- # block.
- #
- # The result is that this is functionally identical to `#fetch`.
- def fetch_multi(*objs, context:, **kwargs)
- objs.flatten!
- map = multi_key_map(objs, context: context)
-
- # TODO: `contextual_cache_key` should be constructed based on the guideline https://docs.gitlab.com/ee/development/redis.html#multi-key-commands.
- Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
- cache.fetch_multi(*map.keys, **kwargs) do |key|
- yield map[key]
- end
- end
- end
-
- # @param objects [Enumerable<Object>] objects which _must_ respond to `#cache_key`
- # @param context [Proc] a proc that can be called to help generate each cache key
- # @return [Hash]
- def multi_key_map(objects, context:)
- objects.index_by do |object|
- contextual_cache_key(object, context)
- end
- end
end
end
end
diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb
index 5c5109f3d21..e38213532ba 100644
--- a/lib/api/helpers/groups_helpers.rb
+++ b/lib/api/helpers/groups_helpers.rb
@@ -30,6 +30,10 @@ module API
params :optional_params_ee do
end
+ params :optional_update_params do
+ optional :prevent_sharing_groups_outside_hierarchy, type: Boolean, desc: 'Prevent sharing groups within this namespace with any groups outside the namespace. Only available on top-level groups.'
+ end
+
params :optional_update_params_ee do
end
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index ca13ea0789a..06539772568 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -6,7 +6,7 @@ module API
#
# The data structures inside this model are returned using class methods,
# allowing EE to extend them where necessary.
- module ServicesHelpers
+ module IntegrationsHelpers
def self.chat_notification_settings
[
{
@@ -159,7 +159,7 @@ module API
].freeze
end
- def self.services
+ def self.integrations
{
'asana' => [
{
@@ -772,7 +772,7 @@ module API
}
end
- def self.service_classes
+ def self.integration_classes
[
::Integrations::Asana,
::Integrations::Assembla,
@@ -799,24 +799,24 @@ module API
::Integrations::Packagist,
::Integrations::PipelinesEmail,
::Integrations::Pivotaltracker,
+ ::Integrations::Prometheus,
::Integrations::Pushover,
::Integrations::Redmine,
::Integrations::Slack,
::Integrations::SlackSlashCommands,
::Integrations::Teamcity,
- ::Integrations::Youtrack,
- ::PrometheusService
+ ::Integrations::Youtrack
]
end
- def self.development_service_classes
+ def self.development_integration_classes
[
::Integrations::MockCi,
- ::MockMonitoringService
+ ::Integrations::MockMonitoring
]
end
end
end
end
-API::Helpers::ServicesHelpers.prepend_mod_with('API::Helpers::ServicesHelpers')
+API::Helpers::IntegrationsHelpers.prepend_mod_with('API::Helpers::IntegrationsHelpers')
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 69a83043617..272452bd8db 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -66,6 +66,7 @@ module API
optional :autoclose_referenced_issues, type: Boolean, desc: 'Flag indication if referenced issues auto-closing is enabled'
optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
optional :packages_enabled, type: Boolean, desc: 'Enable project packages feature'
+ optional :squash_option, type: String, values: %w(never always default_on default_off), desc: 'Squash default for project. One of `never`, `always`, `default_on`, or `default_off`.'
end
params :optional_project_params_ee do
@@ -145,6 +146,7 @@ module API
:request_access_enabled,
:resolve_outdated_diff_discussions,
:restrict_user_defined_variables,
+ :squash_option,
:shared_runners_enabled,
:snippets_access_level,
:tag_list,
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 9ec9b5e1e35..a022d1a56ac 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -14,6 +14,10 @@ module API
ActiveSupport::SecurityUtils.secure_compare(params[:token], Gitlab::CurrentSettings.runners_registration_token)
end
+ def runner_registrar_valid?(type)
+ Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?(type)
+ end
+
def authenticate_runner!
forbidden! unless current_runner
diff --git a/lib/api/helpers/snippets_helpers.rb b/lib/api/helpers/snippets_helpers.rb
index 42f56680ded..2d8c761101a 100644
--- a/lib/api/helpers/snippets_helpers.rb
+++ b/lib/api/helpers/snippets_helpers.rb
@@ -72,22 +72,18 @@ module API
end
def process_create_params(args)
- with_api_params do |api_params|
- args[:snippet_actions] = args.delete(:files)&.map do |file|
- file[:action] = :create
- file.symbolize_keys
- end
-
- args.merge(api_params)
+ args[:snippet_actions] = args.delete(:files)&.map do |file|
+ file[:action] = :create
+ file.symbolize_keys
end
+
+ args
end
def process_update_params(args)
- with_api_params do |api_params|
- args[:snippet_actions] = args.delete(:files)&.map(&:symbolize_keys)
+ args[:snippet_actions] = args.delete(:files)&.map(&:symbolize_keys)
- args.merge(api_params)
- end
+ args
end
def validate_params_for_multiple_files(snippet)
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index ee0ddccc8d4..a06b052847d 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -124,11 +124,6 @@ module API
yield
end
end
-
- # Overridden in EE
- def geo_proxy
- {}
- end
end
namespace 'internal' do
@@ -320,12 +315,6 @@ module API
two_factor_otp_check
end
-
- # Workhorse calls this to determine if it is a Geo secondary site
- # that should proxy requests. FOSS can quickly return empty data.
- get '/geo_proxy', feature_category: :geo_replication do
- geo_proxy
- end
end
end
end
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index c28e2181873..7af5c2ad2ee 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -53,8 +53,6 @@ module API
def check_agent_token
unauthorized! unless agent_token
- forbidden! unless Gitlab::Kas.included_in_gitlab_com_rollout?(agent.project)
-
agent_token.track_usage
end
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 355b5ed3a1f..54013d0e7b4 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -255,9 +255,11 @@ module API
issue_params = convert_parameters_from_legacy_format(issue_params)
begin
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
issue = ::Issues::CreateService.new(project: user_project,
current_user: current_user,
- params: issue_params.merge(request: request, api: true)).execute
+ params: issue_params,
+ spam_params: spam_params).execute
if issue.spam?
render_api_error!({ error: 'Spam detected' }, 400)
@@ -294,13 +296,15 @@ module API
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
- update_params = declared_params(include_missing: false).merge(request: request, api: true)
+ update_params = declared_params(include_missing: false)
update_params = convert_parameters_from_legacy_format(update_params)
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
issue = ::Issues::UpdateService.new(project: user_project,
current_user: current_user,
- params: update_params).execute(issue)
+ params: update_params,
+ spam_params: spam_params).execute(issue)
render_spam_error! if issue.spam?
diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb
index 37199279205..beda4433e4f 100644
--- a/lib/api/job_artifacts.rb
+++ b/lib/api/job_artifacts.rb
@@ -4,7 +4,7 @@ module API
class JobArtifacts < ::API::Base
before { authenticate_non_get! }
- feature_category :continuous_integration
+ feature_category :build_artifacts
# EE::API::JobArtifacts would override the following helpers
helpers do
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index 3580a7b5e24..945cdf3edb2 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -11,11 +11,7 @@ module API
optional :include_merged_yaml, type: Boolean, desc: 'Whether or not to include merged CI config yaml in the response'
end
post '/lint' do
- if Feature.enabled?(:security_ci_lint_authorization)
- unauthorized! if (Gitlab::CurrentSettings.signup_disabled? || Gitlab::CurrentSettings.signup_limited?) && current_user.nil?
- else
- unauthorized! if Gitlab::CurrentSettings.signup_disabled? && current_user.nil?
- end
+ unauthorized! if (Gitlab::CurrentSettings.signup_disabled? || Gitlab::CurrentSettings.signup_limited?) && current_user.nil?
result = Gitlab::Ci::YamlProcessor.new(params[:content], user: current_user).execute
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
index 5bae08d4dae..03d1492908d 100644
--- a/lib/api/nuget_project_packages.rb
+++ b/lib/api/nuget_project_packages.rb
@@ -16,6 +16,7 @@ module API
feature_category :package_registry
PACKAGE_FILENAME = 'package.nupkg'
+ SYMBOL_PACKAGE_FILENAME = 'package.snupkg'
default_format :json
@@ -33,6 +34,10 @@ module API
end
helpers do
+ params :file_params do
+ requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
def project_or_group
authorized_user_project
end
@@ -40,6 +45,49 @@ module API
def snowplow_gitlab_standard_context
{ project: authorized_user_project, namespace: authorized_user_project.namespace }
end
+
+ def authorize_nuget_upload
+ authorize_workhorse!(
+ subject: project_or_group,
+ has_length: false,
+ maximum_size: project_or_group.actual_limits.nuget_max_file_size
+ )
+ end
+
+ def temp_file_name(symbol_package)
+ return ::Packages::Nuget::TEMPORARY_SYMBOL_PACKAGE_NAME if symbol_package
+
+ ::Packages::Nuget::TEMPORARY_PACKAGE_NAME
+ end
+
+ def file_name(symbol_package)
+ return SYMBOL_PACKAGE_FILENAME if symbol_package
+
+ PACKAGE_FILENAME
+ end
+
+ def upload_nuget_package_file(symbol_package: false)
+ authorize_upload!(project_or_group)
+ bad_request!('File is too large') if project_or_group.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
+
+ file_params = params.merge(
+ file: params[:package],
+ file_name: file_name(symbol_package)
+ )
+
+ package = ::Packages::CreateTemporaryPackageService.new(
+ project_or_group, current_user, declared_params.merge(build: current_authenticated_job)
+ ).execute(:nuget, name: temp_file_name(symbol_package))
+
+ package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job))
+ .execute
+
+ yield(package) if block_given?
+
+ ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
+
+ created!
+ end
end
params do
@@ -55,40 +103,54 @@ module API
end
params do
- requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ use :file_params
end
put do
- authorize_upload!(project_or_group)
- bad_request!('File is too large') if project_or_group.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
-
- file_params = params.merge(
- file: params[:package],
- file_name: PACKAGE_FILENAME
- )
-
- package = ::Packages::CreateTemporaryPackageService.new(
- project_or_group, current_user, declared_params.merge(build: current_authenticated_job)
- ).execute(:nuget, name: ::Packages::Nuget::TEMPORARY_PACKAGE_NAME)
-
- package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job))
- .execute
+ upload_nuget_package_file do |package|
+ track_package_event(
+ 'push_package',
+ :nuget,
+ category: 'API::NugetPackages',
+ user: current_user,
+ project: package.project,
+ namespace: package.project.namespace
+ )
+ end
+ rescue ObjectStorage::RemoteStoreError => e
+ Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project_or_group.id })
- track_package_event('push_package', :nuget, category: 'API::NugetPackages', user: current_user, project: package.project, namespace: package.project.namespace)
+ forbidden!
+ end
+ put 'authorize' do
+ authorize_nuget_upload
+ end
- ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
+ # https://docs.microsoft.com/en-us/nuget/api/symbol-package-publish-resource
+ desc 'The NuGet Symbol Package Publish endpoint' do
+ detail 'This feature was introduced in GitLab 14.1'
+ end
- created!
+ params do
+ use :file_params
+ end
+ put 'symbolpackage' do
+ upload_nuget_package_file(symbol_package: true) do |package|
+ track_package_event(
+ 'push_symbol_package',
+ :nuget,
+ category: 'API::NugetPackages',
+ user: current_user,
+ project: package.project,
+ namespace: package.project.namespace
+ )
+ end
rescue ObjectStorage::RemoteStoreError => e
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project_or_group.id })
forbidden!
end
- put 'authorize' do
- authorize_workhorse!(
- subject: project_or_group,
- has_length: false,
- maximum_size: project_or_group.actual_limits.nuget_max_file_size
- )
+ put 'symbolpackage/authorize' do
+ authorize_nuget_upload
end
# https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
@@ -115,14 +177,20 @@ module API
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
end
- get '*package_version/*package_filename', format: :nupkg do
+ get '*package_version/*package_filename', format: [:nupkg, :snupkg] do
filename = "#{params[:package_filename]}.#{params[:format]}"
package_file = ::Packages::PackageFileFinder.new(find_package(params[:package_name], params[:package_version]), filename, with_file_name_like: true)
.execute
not_found!('Package') unless package_file
- track_package_event('pull_package', :nuget, category: 'API::NugetPackages', project: package_file.project, namespace: package_file.project.namespace)
+ track_package_event(
+ params[:format] == 'snupkg' ? 'pull_symbol_package' : 'pull_package',
+ :nuget,
+ category: 'API::NugetPackages',
+ project: package_file.project,
+ namespace: package_file.project.namespace
+ )
# nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
present_carrierwave_file!(package_file.file, supports_direct_download: false)
diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb
index 35a68ec4e18..54c0a0628a7 100644
--- a/lib/api/project_packages.rb
+++ b/lib/api/project_packages.rb
@@ -71,9 +71,7 @@ module API
.new(user_project, params[:package_id]).execute
destroy_conditionally!(package) do |package|
- if package.destroy
- package.sync_maven_metadata(current_user)
- end
+ ::Packages::DestroyPackageService.new(container: package, current_user: current_user).execute
end
end
end
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 084492fd503..fdbfdf1f7a9 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -75,7 +75,8 @@ module API
snippet_params = process_create_params(declared_params(include_missing: false))
- service_response = ::Snippets::CreateService.new(project: user_project, current_user: current_user, params: snippet_params).execute
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
+ service_response = ::Snippets::CreateService.new(project: user_project, current_user: current_user, params: snippet_params, spam_params: spam_params).execute
snippet = service_response.payload[:snippet]
if service_response.success?
@@ -116,7 +117,8 @@ module API
snippet_params = process_update_params(declared_params(include_missing: false))
- service_response = ::Snippets::UpdateService.new(project: user_project, current_user: current_user, params: snippet_params).execute(snippet)
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
+ service_response = ::Snippets::UpdateService.new(project: user_project, current_user: current_user, params: snippet_params, spam_params: spam_params).execute(snippet)
snippet = service_response.payload[:snippet]
if service_response.success?
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 83c335a3248..3b1d239398f 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -157,6 +157,8 @@ module API
[options[:with].prepare_relation(projects, options), options]
end
+ Preloaders::UserMaxAccessLevelInProjectsPreloader.new(records, current_user).execute if current_user
+
present records, options
end
@@ -608,6 +610,10 @@ module API
users = users.search(params[:search]) if params[:search].present?
users = users.where_not_in(params[:skip_users]) if params[:skip_users].present?
+ if Feature.enabled?(:sort_by_project_users_by_project_authorizations_user_id, user_project, default_enabled: :yaml)
+ users = users.order('project_authorizations.user_id' => :asc) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
present paginate(users), with: Entities::UserBasic
end
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index 7cd8b442706..3b7e2b4bd27 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -62,6 +62,8 @@ module API
get ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
+ not_found! unless release
+
present release, with: Entities::Release, current_user: current_user, include_html_description: params[:include_html_description]
end
@@ -177,7 +179,7 @@ module API
end
def authorize_download_code!
- authorize! :download_code, release
+ authorize! :download_code, user_project
end
def authorize_create_evidence!
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index a5234828de3..f274406e225 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -120,24 +120,28 @@ module API
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
- 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)
+ ff_enabled = Feature.enabled?(:api_caching_rate_limit_repository_compare, user_project, default_enabled: :yaml)
+
+ 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
+ else
+ target_project = user_project
end
- else
- target_project = user_project
- end
- compare = CompareService.new(user_project, params[:to]).execute(target_project, params[:from], straight: params[:straight])
+ compare = CompareService.new(user_project, params[:to]).execute(target_project, params[:from], straight: params[:straight])
- if compare
- present compare, with: Entities::Compare
- else
- not_found!("Ref")
+ if compare
+ present compare, with: Entities::Compare
+ else
+ not_found!("Ref")
+ end
end
end
diff --git a/lib/api/resource_access_tokens.rb b/lib/api/resource_access_tokens.rb
index 705e4778c83..f42acc6b2eb 100644
--- a/lib/api/resource_access_tokens.rb
+++ b/lib/api/resource_access_tokens.rb
@@ -21,9 +21,10 @@ module API
next unauthorized! unless current_user.can?(:read_resource_access_tokens, resource)
- tokens = PersonalAccessTokensFinder.new({ user: resource.bots, impersonation: false }).execute
+ tokens = PersonalAccessTokensFinder.new({ user: resource.bots, impersonation: false }).execute.preload_users
- present paginate(tokens), with: Entities::PersonalAccessToken
+ resource.project_members.load
+ present paginate(tokens), with: Entities::ResourceAccessToken, project: resource
end
desc 'Revoke a resource access token' do
@@ -57,6 +58,7 @@ module API
requires :id, type: String, desc: "The #{source_type} ID"
requires :name, type: String, desc: "Resource access token name"
requires :scopes, type: Array[String], desc: "The permissions of the token"
+ optional :access_level, type: Integer, desc: "The access level of the token in the project"
optional :expires_at, type: Date, desc: "The expiration date of the token"
end
post ':id/access_tokens' do
@@ -69,7 +71,7 @@ module API
).execute
if token_response.success?
- present token_response.payload[:access_token], with: Entities::PersonalAccessTokenWithToken
+ present token_response.payload[:access_token], with: Entities::ResourceAccessTokenWithToken, project: resource
else
bad_request!(token_response.message)
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 8a7abe721dd..a37b6f4626a 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -3,11 +3,11 @@ module API
class Services < ::API::Base
feature_category :integrations
- services = Helpers::ServicesHelpers.services
- service_classes = Helpers::ServicesHelpers.service_classes
+ integrations = Helpers::IntegrationsHelpers.integrations
+ integration_classes = Helpers::IntegrationsHelpers.integration_classes
if Rails.env.development?
- services['mock-ci'] = [
+ integrations['mock-ci'] = [
{
required: true,
name: :mock_service_url,
@@ -15,28 +15,27 @@ module API
desc: 'URL to the mock service'
}
]
- services['mock-deployment'] = []
- services['mock-monitoring'] = []
+ integrations['mock-deployment'] = []
+ integrations['mock-monitoring'] = []
- service_classes += Helpers::ServicesHelpers.development_service_classes
+ integration_classes += Helpers::IntegrationsHelpers.development_integration_classes
end
- SERVICES = services.freeze
- SERVICE_CLASSES = service_classes.freeze
+ INTEGRATIONS = integrations.freeze
- SERVICE_CLASSES.each do |service|
- event_names = service.try(:event_names) || next
+ integration_classes.each do |integration|
+ event_names = integration.try(:event_names) || next
event_names.each do |event_name|
- SERVICES[service.to_param.tr("_", "-")] << {
+ INTEGRATIONS[integration.to_param.tr("_", "-")] << {
required: false,
name: event_name.to_sym,
type: String,
- desc: service.event_description(event_name)
+ desc: IntegrationsHelper.integration_event_description(integration, event_name)
}
end
end
- TRIGGER_SERVICES = {
+ TRIGGER_INTEGRATIONS = {
'mattermost-slash-commands' => [
{
name: :token,
@@ -61,24 +60,24 @@ module API
before { authorize_admin_project }
helpers do
- def service_attributes(service)
- service.fields.inject([]) do |arr, hash|
+ def integration_attributes(integration)
+ integration.fields.inject([]) do |arr, hash|
arr << hash[:name].to_sym
end
end
end
- desc 'Get all active project services' do
- success Entities::ProjectServiceBasic
+ desc 'Get all active project integrations' do
+ success Entities::ProjectIntegrationBasic
end
get ":id/services" do
- services = user_project.integrations.active
+ integrations = user_project.integrations.active
- present services, with: Entities::ProjectServiceBasic
+ present integrations, with: Entities::ProjectIntegrationBasic
end
- SERVICES.each do |service_slug, settings|
- desc "Set #{service_slug} service for project"
+ INTEGRATIONS.each do |slug, settings|
+ desc "Set #{slug} integration for project"
params do
settings.each do |setting|
if setting[:required]
@@ -88,56 +87,52 @@ module API
end
end
end
- put ":id/services/#{service_slug}" do
- service = user_project.find_or_initialize_service(service_slug.underscore)
- service_params = declared_params(include_missing: false).merge(active: true)
+ put ":id/services/#{slug}" do
+ integration = user_project.find_or_initialize_integration(slug.underscore)
+ params = declared_params(include_missing: false).merge(active: true)
- if service.update(service_params)
- present service, with: Entities::ProjectService
+ if integration.update(params)
+ present integration, with: Entities::ProjectIntegration
else
render_api_error!('400 Bad Request', 400)
end
end
end
- desc "Delete a service for project"
+ desc "Delete an integration from a project"
params do
- requires :service_slug, type: String, values: SERVICES.keys, desc: 'The name of the service'
+ requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the service'
end
- delete ":id/services/:service_slug" do
- service = user_project.find_or_initialize_service(params[:service_slug].underscore)
+ delete ":id/services/:slug" do
+ integration = user_project.find_or_initialize_integration(params[:slug].underscore)
- destroy_conditionally!(service) do
- attrs = service_attributes(service).inject({}) do |hash, key|
- hash.merge!(key => nil)
- end
+ destroy_conditionally!(integration) do
+ attrs = integration_attributes(integration).index_with { nil }.merge(active: false)
- unless service.update(attrs.merge(active: false))
- render_api_error!('400 Bad Request', 400)
- end
+ render_api_error!('400 Bad Request', 400) unless integration.update(attrs)
end
end
- desc 'Get the service settings for project' do
- success Entities::ProjectService
+ desc 'Get the integration settings for a project' do
+ success Entities::ProjectIntegration
end
params do
- requires :service_slug, type: String, values: SERVICES.keys, desc: 'The name of the service'
+ requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the service'
end
- get ":id/services/:service_slug" do
- integration = user_project.find_or_initialize_service(params[:service_slug].underscore)
+ get ":id/services/:slug" do
+ integration = user_project.find_or_initialize_integration(params[:slug].underscore)
not_found!('Service') unless integration&.persisted?
- present integration, with: Entities::ProjectService
+ present integration, with: Entities::ProjectIntegration
end
end
- TRIGGER_SERVICES.each do |service_slug, settings|
+ TRIGGER_INTEGRATIONS.each do |integration_slug, settings|
helpers do
- def slash_command_service(project, service_slug, params)
- project.integrations.active.find do |service|
- service.try(:token) == params[:token] && service.to_param == service_slug.underscore
+ def slash_command_integration(project, integration_slug, params)
+ project.integrations.active.find do |integration|
+ integration.try(:token) == params[:token] && integration.to_param == integration_slug.underscore
end
end
end
@@ -146,7 +141,7 @@ module API
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc "Trigger a slash command for #{service_slug}" do
+ desc "Trigger a slash command for #{integration_slug}" do
detail 'Added in GitLab 8.13'
end
params do
@@ -154,14 +149,14 @@ module API
requires setting[:name], type: setting[:type], desc: setting[:desc]
end
end
- post ":id/services/#{service_slug.underscore}/trigger" do
+ post ":id/services/#{integration_slug.underscore}/trigger" do
project = find_project(params[:id])
# This is not accurate, but done to prevent leakage of the project names
not_found!('Service') unless project
- service = slash_command_service(project, service_slug, params)
- result = service.try(:trigger, params)
+ integration = slash_command_integration(project, integration_slug, params)
+ result = integration.try(:trigger, params)
if result
status result[:status] || 200
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index b4f8320cb74..952bf09b1b1 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -160,6 +160,10 @@ module API
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated'
optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5
+ optional :mailgun_events_enabled, type: Grape::API::Boolean, desc: 'Enable Mailgun event receiver'
+ given mailgun_events_enabled: ->(val) { val } do
+ requires :mailgun_signing_key, type: String, desc: 'The Mailgun HTTP webhook signing key for receiving events from webhook'
+ end
optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking'
given snowplow_enabled: ->(val) { val } do
requires :snowplow_collector_hostname, type: String, desc: 'The Snowplow collector hostname'
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index b506192fe1c..f1ec1024492 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -84,7 +84,8 @@ module API
attrs = process_create_params(declared_params(include_missing: false))
- service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs).execute
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
+ service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute
snippet = service_response.payload[:snippet]
if service_response.success?
@@ -126,7 +127,8 @@ module API
attrs = process_update_params(declared_params(include_missing: false))
- service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs).execute(snippet)
+ spam_params = ::Spam::SpamParams.new_from_request(request: request)
+ service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute(snippet)
snippet = service_response.payload[:snippet]
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index fe23a111b7f..e4133713c1f 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -31,6 +31,7 @@ module API
optional :push_events, type: Boolean, desc: "Trigger hook on push events"
optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events"
optional :merge_requests_events, type: Boolean, desc: "Trigger hook on tag push events"
+ optional :repository_update_events, type: Boolean, desc: "Trigger hook on repository update events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
end
post do
diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb
index 7deec15dcac..43c75206b88 100644
--- a/lib/api/usage_data.rb
+++ b/lib/api/usage_data.rb
@@ -4,7 +4,7 @@ module API
class UsageData < ::API::Base
before { authenticate_non_get! }
- feature_category :usage_ping
+ feature_category :service_ping
namespace 'usage_data' do
before do
diff --git a/lib/api/usage_data_non_sql_metrics.rb b/lib/api/usage_data_non_sql_metrics.rb
index 63a14a223f5..d9e0d153e58 100644
--- a/lib/api/usage_data_non_sql_metrics.rb
+++ b/lib/api/usage_data_non_sql_metrics.rb
@@ -4,7 +4,7 @@ module API
class UsageDataNonSqlMetrics < ::API::Base
before { authenticated_as_admin! }
- feature_category :usage_ping
+ feature_category :service_ping
namespace 'usage_data' do
before do
diff --git a/lib/api/usage_data_queries.rb b/lib/api/usage_data_queries.rb
index 0ad9ad7650c..22e83fe0294 100644
--- a/lib/api/usage_data_queries.rb
+++ b/lib/api/usage_data_queries.rb
@@ -4,7 +4,7 @@ module API
class UsageDataQueries < ::API::Base
before { authenticated_as_admin! }
- feature_category :usage_ping
+ feature_category :service_ping
namespace 'usage_data' do
before do
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index 8b0745c6b5b..75df0e050a6 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -7,7 +7,7 @@ module API
before { authenticate! }
before { authorize! :admin_build, user_project }
- feature_category :continuous_integration
+ feature_category :pipeline_authoring
helpers Helpers::VariablesHelpers