summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/access_requests.rb2
-rw-r--r--lib/api/admin/ci/variables.rb2
-rw-r--r--lib/api/admin/instance_clusters.rb4
-rw-r--r--lib/api/admin/sidekiq.rb2
-rw-r--r--lib/api/api.rb10
-rw-r--r--lib/api/api_guard.rb39
-rw-r--r--lib/api/appearance.rb2
-rw-r--r--lib/api/applications.rb2
-rw-r--r--lib/api/avatar.rb2
-rw-r--r--lib/api/award_emoji.rb2
-rw-r--r--lib/api/badges.rb2
-rw-r--r--lib/api/base.rb6
-rw-r--r--lib/api/boards.rb2
-rw-r--r--lib/api/boards_responses.rb2
-rw-r--r--lib/api/branches.rb2
-rw-r--r--lib/api/broadcast_messages.rb2
-rw-r--r--lib/api/ci/pipeline_schedules.rb2
-rw-r--r--lib/api/ci/pipelines.rb6
-rw-r--r--lib/api/ci/runner.rb6
-rw-r--r--lib/api/ci/runners.rb2
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/commits.rb18
-rw-r--r--lib/api/composer_packages.rb8
-rw-r--r--lib/api/conan_instance_packages.rb2
-rw-r--r--lib/api/conan_package_endpoints.rb2
-rw-r--r--lib/api/conan_project_packages.rb2
-rw-r--r--lib/api/container_registry_event.rb2
-rw-r--r--lib/api/debian_group_packages.rb21
-rw-r--r--lib/api/debian_package_endpoints.rb124
-rw-r--r--lib/api/debian_project_packages.rb56
-rw-r--r--lib/api/deploy_keys.rb2
-rw-r--r--lib/api/deploy_tokens.rb2
-rw-r--r--lib/api/deployments.rb2
-rw-r--r--lib/api/discussions.rb2
-rw-r--r--lib/api/entities/ci/lint/result.rb16
-rw-r--r--lib/api/entities/cluster.rb2
-rw-r--r--lib/api/entities/container_registry.rb1
-rw-r--r--lib/api/entities/feature_flag.rb16
-rw-r--r--lib/api/entities/feature_flag/detailed_legacy_scope.rb11
-rw-r--r--lib/api/entities/feature_flag/legacy_scope.rb16
-rw-r--r--lib/api/entities/feature_flag/scope.rb12
-rw-r--r--lib/api/entities/feature_flag/strategy.rb14
-rw-r--r--lib/api/entities/feature_flag/user_list.rb27
-rw-r--r--lib/api/entities/job_request/cache.rb2
-rw-r--r--lib/api/entities/member.rb1
-rw-r--r--lib/api/entities/package.rb14
-rw-r--r--lib/api/entities/project.rb1
-rw-r--r--lib/api/entities/snippet.rb6
-rw-r--r--lib/api/entities/unleash_feature.rb32
-rw-r--r--lib/api/entities/unleash_gitlab_user_list_strategy.rb14
-rw-r--r--lib/api/entities/unleash_legacy_strategy.rb14
-rw-r--r--lib/api/entities/unleash_strategy.rb10
-rw-r--r--lib/api/entities/user_with_admin.rb2
-rw-r--r--lib/api/environments.rb2
-rw-r--r--lib/api/error_tracking.rb2
-rw-r--r--lib/api/events.rb2
-rw-r--r--lib/api/feature_flag_scopes.rb158
-rw-r--r--lib/api/feature_flags.rb266
-rw-r--r--lib/api/feature_flags_user_lists.rb100
-rw-r--r--lib/api/features.rb2
-rw-r--r--lib/api/files.rb2
-rw-r--r--lib/api/freeze_periods.rb2
-rw-r--r--lib/api/generic_packages.rb90
-rw-r--r--lib/api/github/entities.rb4
-rwxr-xr-xlib/api/go_proxy.rb2
-rw-r--r--lib/api/group_boards.rb2
-rw-r--r--lib/api/group_clusters.rb4
-rw-r--r--lib/api/group_container_repositories.rb6
-rw-r--r--lib/api/group_export.rb2
-rw-r--r--lib/api/group_import.rb2
-rw-r--r--lib/api/group_labels.rb2
-rw-r--r--lib/api/group_milestones.rb2
-rw-r--r--lib/api/group_packages.rb2
-rw-r--r--lib/api/group_variables.rb2
-rw-r--r--lib/api/groups.rb22
-rw-r--r--lib/api/helpers.rb3
-rw-r--r--lib/api/helpers/groups_helpers.rb1
-rw-r--r--lib/api/helpers/merge_requests_helpers.rb7
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb4
-rw-r--r--lib/api/helpers/packages/dependency_proxy_helpers.rb1
-rw-r--r--lib/api/helpers/packages_helpers.rb5
-rw-r--r--lib/api/helpers/pagination.rb4
-rw-r--r--lib/api/helpers/presentable.rb2
-rw-r--r--lib/api/helpers/projects_helpers.rb10
-rw-r--r--lib/api/helpers/runner.rb4
-rw-r--r--lib/api/helpers/services_helpers.rb6
-rw-r--r--lib/api/helpers/settings_helpers.rb1
-rw-r--r--lib/api/helpers/snippets_helpers.rb2
-rw-r--r--lib/api/import_bitbucket_server.rb2
-rw-r--r--lib/api/import_github.rb2
-rw-r--r--lib/api/internal/base.rb92
-rw-r--r--lib/api/internal/kubernetes.rb2
-rw-r--r--lib/api/internal/lfs.rb54
-rw-r--r--lib/api/internal/pages.rb2
-rw-r--r--lib/api/issue_links.rb2
-rw-r--r--lib/api/issues.rb7
-rw-r--r--lib/api/job_artifacts.rb2
-rw-r--r--lib/api/jobs.rb2
-rw-r--r--lib/api/keys.rb2
-rw-r--r--lib/api/labels.rb2
-rw-r--r--lib/api/lint.rb38
-rw-r--r--lib/api/markdown.rb2
-rw-r--r--lib/api/maven_packages.rb18
-rw-r--r--lib/api/members.rb32
-rw-r--r--lib/api/merge_request_approvals.rb2
-rw-r--r--lib/api/merge_request_diffs.rb2
-rw-r--r--lib/api/merge_requests.rb2
-rw-r--r--lib/api/metrics/dashboard/annotations.rb2
-rw-r--r--lib/api/metrics/user_starred_dashboards.rb2
-rw-r--r--lib/api/namespaces.rb6
-rw-r--r--lib/api/notes.rb2
-rw-r--r--lib/api/notification_settings.rb2
-rw-r--r--lib/api/npm_packages.rb6
-rw-r--r--lib/api/nuget_packages.rb12
-rw-r--r--lib/api/package_files.rb2
-rw-r--r--lib/api/pages.rb2
-rw-r--r--lib/api/pages_domains.rb2
-rw-r--r--lib/api/project_clusters.rb4
-rw-r--r--lib/api/project_container_repositories.rb14
-rw-r--r--lib/api/project_events.rb2
-rw-r--r--lib/api/project_export.rb4
-rw-r--r--lib/api/project_hooks.rb6
-rw-r--r--lib/api/project_import.rb4
-rw-r--r--lib/api/project_milestones.rb2
-rw-r--r--lib/api/project_packages.rb2
-rw-r--r--lib/api/project_repository_storage_moves.rb4
-rw-r--r--lib/api/project_snapshots.rb2
-rw-r--r--lib/api/project_snippets.rb14
-rw-r--r--lib/api/project_statistics.rb2
-rw-r--r--lib/api/project_templates.rb2
-rw-r--r--lib/api/projects.rb4
-rw-r--r--lib/api/protected_branches.rb2
-rw-r--r--lib/api/protected_tags.rb2
-rw-r--r--lib/api/pypi_packages.rb10
-rw-r--r--lib/api/release/links.rb2
-rw-r--r--lib/api/releases.rb16
-rw-r--r--lib/api/remote_mirrors.rb2
-rw-r--r--lib/api/repositories.rb2
-rw-r--r--lib/api/resource_label_events.rb2
-rw-r--r--lib/api/resource_milestone_events.rb2
-rw-r--r--lib/api/resource_state_events.rb2
-rw-r--r--lib/api/search.rb16
-rw-r--r--lib/api/services.rb2
-rw-r--r--lib/api/settings.rb11
-rw-r--r--lib/api/sidekiq_metrics.rb2
-rw-r--r--lib/api/snippets.rb16
-rw-r--r--lib/api/statistics.rb2
-rw-r--r--lib/api/submodules.rb2
-rw-r--r--lib/api/subscriptions.rb2
-rw-r--r--lib/api/suggestions.rb2
-rw-r--r--lib/api/system_hooks.rb6
-rw-r--r--lib/api/tags.rb2
-rw-r--r--lib/api/templates.rb2
-rw-r--r--lib/api/terraform/state.rb2
-rw-r--r--lib/api/terraform/state_version.rb68
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/api/triggers.rb2
-rw-r--r--lib/api/unleash.rb77
-rw-r--r--lib/api/usage_data.rb4
-rw-r--r--lib/api/user_counts.rb2
-rw-r--r--lib/api/users.rb33
-rw-r--r--lib/api/v3/github.rb4
-rw-r--r--lib/api/variables.rb2
-rw-r--r--lib/api/version.rb2
-rw-r--r--lib/api/wikis.rb2
165 files changed, 1695 insertions, 266 deletions
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index 5305b25538f..7e3d70a210a 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class AccessRequests < Grape::API::Instance
+ class AccessRequests < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/admin/ci/variables.rb b/lib/api/admin/ci/variables.rb
index 8721d94d642..44c389d6f94 100644
--- a/lib/api/admin/ci/variables.rb
+++ b/lib/api/admin/ci/variables.rb
@@ -3,7 +3,7 @@
module API
module Admin
module Ci
- class Variables < Grape::API::Instance
+ class Variables < ::API::Base
include PaginationParams
before { authenticated_as_admin! }
diff --git a/lib/api/admin/instance_clusters.rb b/lib/api/admin/instance_clusters.rb
index 8208d10c089..ce1bdd65eff 100644
--- a/lib/api/admin/instance_clusters.rb
+++ b/lib/api/admin/instance_clusters.rb
@@ -2,7 +2,7 @@
module API
module Admin
- class InstanceClusters < Grape::API::Instance
+ class InstanceClusters < ::API::Base
include PaginationParams
before do
@@ -37,6 +37,7 @@ module API
requires :name, type: String, desc: 'Cluster name'
optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true'
optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster'
+ optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :domain, type: String, desc: 'Cluster base domain'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true'
@@ -70,6 +71,7 @@ module API
optional :name, type: String, desc: 'Cluster name'
optional :enabled, type: Boolean, desc: 'Enable or disable Gitlab\'s connection to your Kubernetes cluster'
optional :environment_scope, type: String, desc: 'The associated environment to the cluster'
+ optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :domain, type: String, desc: 'Cluster base domain'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
diff --git a/lib/api/admin/sidekiq.rb b/lib/api/admin/sidekiq.rb
index f4c84f2eee8..c2e9de5fb4e 100644
--- a/lib/api/admin/sidekiq.rb
+++ b/lib/api/admin/sidekiq.rb
@@ -2,7 +2,7 @@
module API
module Admin
- class Sidekiq < Grape::API::Instance
+ class Sidekiq < ::API::Base
before { authenticated_as_admin! }
namespace 'admin' do
diff --git a/lib/api/api.rb b/lib/api/api.rb
index b37751e1b47..84b4d5a5835 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class API < Grape::API::Instance
+ class API < ::API::Base
include APIGuard
LOG_FILENAME = Rails.root.join("log", "api_json.log")
@@ -153,6 +153,9 @@ module API
mount ::API::Environments
mount ::API::ErrorTracking
mount ::API::Events
+ mount ::API::FeatureFlags
+ mount ::API::FeatureFlagScopes
+ mount ::API::FeatureFlagsUserLists
mount ::API::Features
mount ::API::Files
mount ::API::FreezePeriods
@@ -196,6 +199,8 @@ module API
mount ::API::ComposerPackages
mount ::API::ConanProjectPackages
mount ::API::ConanInstancePackages
+ mount ::API::DebianGroupPackages
+ mount ::API::DebianProjectPackages
mount ::API::MavenPackages
mount ::API::NpmPackages
mount ::API::GenericPackages
@@ -216,6 +221,7 @@ module API
mount ::API::ProjectStatistics
mount ::API::ProjectTemplates
mount ::API::Terraform::State
+ mount ::API::Terraform::StateVersion
mount ::API::ProtectedBranches
mount ::API::ProtectedTags
mount ::API::Releases
@@ -236,6 +242,7 @@ module API
mount ::API::Templates
mount ::API::Todos
mount ::API::Triggers
+ mount ::API::Unleash
mount ::API::UsageData
mount ::API::UserCounts
mount ::API::Users
@@ -245,6 +252,7 @@ module API
end
mount ::API::Internal::Base
+ mount ::API::Internal::Lfs
mount ::API::Internal::Pages
mount ::API::Internal::Kubernetes
diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index bf5044b4832..0a486307653 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -19,6 +19,7 @@ module API
end
use AdminModeMiddleware
+ use ResponseCoercerMiddleware
helpers HelperMethods
@@ -188,6 +189,44 @@ module API
end
end
+ # Prior to Rack v2.1.x, returning a body of [nil] or [201] worked
+ # because the body was coerced to a string. However, this no longer
+ # works in Rack v2.1.0+. The Rack spec
+ # (https://github.com/rack/rack/blob/master/SPEC.rdoc#the-body-)
+ # says:
+ #
+ # The Body must respond to `each` and must only yield String values
+ #
+ # Because it's easy to return the wrong body type, this middleware
+ # will:
+ #
+ # 1. Inspect each element of the body if it is an Array.
+ # 2. Coerce each value to a string if necessary.
+ # 3. Flag a test and development error.
+ class ResponseCoercerMiddleware < ::Grape::Middleware::Base
+ def call(env)
+ response = super(env)
+
+ status = response[0]
+ body = response[2]
+
+ return response if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY[status]
+ return response unless body.is_a?(Array)
+
+ body.map! do |part|
+ if part.is_a?(String)
+ part
+ else
+ err = ArgumentError.new("The response body should be a String, but it is of type #{part.class}")
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(err)
+ part.to_s
+ end
+ end
+
+ response
+ end
+ end
+
class AdminModeMiddleware < ::Grape::Middleware::Base
def after
# Use a Grape middleware since the Grape `after` blocks might run
diff --git a/lib/api/appearance.rb b/lib/api/appearance.rb
index f98004af480..00b495bbc1e 100644
--- a/lib/api/appearance.rb
+++ b/lib/api/appearance.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Appearance < Grape::API::Instance
+ class Appearance < ::API::Base
before { authenticated_as_admin! }
helpers do
diff --git a/lib/api/applications.rb b/lib/api/applications.rb
index 4f2c3ee79ef..2afe8763d9d 100644
--- a/lib/api/applications.rb
+++ b/lib/api/applications.rb
@@ -2,7 +2,7 @@
module API
# External applications API
- class Applications < Grape::API::Instance
+ class Applications < ::API::Base
before { authenticated_as_admin! }
resource :applications do
diff --git a/lib/api/avatar.rb b/lib/api/avatar.rb
index 9501e777fff..5a9b9940fcf 100644
--- a/lib/api/avatar.rb
+++ b/lib/api/avatar.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Avatar < Grape::API::Instance
+ class Avatar < ::API::Base
resource :avatar do
desc 'Return avatar url for a user' do
success Entities::Avatar
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index 0a3df3ed96e..6d40ae8f5ff 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class AwardEmoji < Grape::API::Instance
+ class AwardEmoji < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/badges.rb b/lib/api/badges.rb
index f9728ffc446..fc00594c9ec 100644
--- a/lib/api/badges.rb
+++ b/lib/api/badges.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Badges < Grape::API::Instance
+ class Badges < ::API::Base
include PaginationParams
before { authenticate_non_get! }
diff --git a/lib/api/base.rb b/lib/api/base.rb
new file mode 100644
index 00000000000..e174cef3bad
--- /dev/null
+++ b/lib/api/base.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module API
+ class Base < Grape::API::Instance # rubocop:disable API/Base
+ end
+end
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index 1f5086127a8..d2d1628aff4 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Boards < Grape::API::Instance
+ class Boards < ::API::Base
include BoardsResponses
include PaginationParams
diff --git a/lib/api/boards_responses.rb b/lib/api/boards_responses.rb
index 68497a08fb8..6a86c02bf4a 100644
--- a/lib/api/boards_responses.rb
+++ b/lib/api/boards_responses.rb
@@ -45,7 +45,7 @@ module API
def destroy_list(list)
destroy_conditionally!(list) do |list|
service = ::Boards::Lists::DestroyService.new(board_parent, current_user)
- unless service.execute(list)
+ if service.execute(list).error?
render_api_error!({ error: 'List could not be deleted!' }, 400)
end
end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 44f7610384e..37cce6eafba 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -3,7 +3,7 @@
require 'mime/types'
module API
- class Branches < Grape::API::Instance
+ class Branches < ::API::Base
include PaginationParams
BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index dcf950d7a03..8ce7694bbfd 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class BroadcastMessages < Grape::API::Instance
+ class BroadcastMessages < ::API::Base
include PaginationParams
resource :broadcast_messages do
diff --git a/lib/api/ci/pipeline_schedules.rb b/lib/api/ci/pipeline_schedules.rb
index 1afdb0ad34c..18caf85f109 100644
--- a/lib/api/ci/pipeline_schedules.rb
+++ b/lib/api/ci/pipeline_schedules.rb
@@ -2,7 +2,7 @@
module API
module Ci
- class PipelineSchedules < Grape::API::Instance
+ class PipelineSchedules < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index 045f81074a7..61e03ed1a95 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -2,7 +2,7 @@
module API
module Ci
- class Pipelines < Grape::API::Instance
+ class Pipelines < ::API::Base
include PaginationParams
before { authenticate_non_get! }
@@ -128,7 +128,7 @@ module API
pipeline = user_project.all_pipelines.find(params[:pipeline_id])
- if Feature.enabled?(:ci_jobs_finder_refactor)
+ if Feature.enabled?(:ci_jobs_finder_refactor, default_enabled: true)
builds = ::Ci::JobsFinder
.new(current_user: current_user, pipeline: pipeline, params: params)
.execute
@@ -157,7 +157,7 @@ module API
pipeline = user_project.all_pipelines.find(params[:pipeline_id])
- if Feature.enabled?(:ci_jobs_finder_refactor)
+ if Feature.enabled?(:ci_jobs_finder_refactor, default_enabled: true)
bridges = ::Ci::JobsFinder
.new(current_user: current_user, pipeline: pipeline, params: params, type: ::Ci::Bridge)
.execute
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 08903dce3dc..ef679147c9f 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -2,7 +2,7 @@
module API
module Ci
- class Runner < Grape::API::Instance
+ class Runner < ::API::Base
helpers ::API::Helpers::Runner
resource :runners do
@@ -72,6 +72,7 @@ module API
post '/verify' do
authenticate_runner!
status 200
+ body "200"
end
end
@@ -181,7 +182,9 @@ module API
.new(job, declared_params(include_missing: false))
service.execute.then do |result|
+ header 'X-GitLab-Trace-Update-Interval', result.backoff
status result.status
+ body result.status.to_s
end
end
@@ -292,6 +295,7 @@ module API
if result[:status] == :success
status :created
+ body "201"
else
render_api_error!(result[:message], result[:http_status])
end
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 7bca72f8028..d37f10fe631 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -2,7 +2,7 @@
module API
module Ci
- class Runners < Grape::API::Instance
+ class Runners < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 9f5a6e87505..af103b8c1f8 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -3,7 +3,7 @@
require 'mime/types'
module API
- class CommitStatuses < Grape::API::Instance
+ class CommitStatuses < ::API::Base
params do
requires :id, type: String, desc: 'The ID of a project'
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 20877fb5c5f..582ccd41847 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -3,7 +3,7 @@
require 'mime/types'
module API
- class Commits < Grape::API::Instance
+ class Commits < ::API::Base
include PaginationParams
before do
@@ -62,19 +62,15 @@ module API
first_parent: first_parent,
order: order)
- commit_count =
- if all || path || before || after || first_parent
- user_project.repository.count_commits(ref: ref, path: path, before: before, after: after, all: all, first_parent: first_parent)
- else
- # Cacheable commit count.
- user_project.repository.commit_count_for_ref(ref)
- end
+ serializer = with_stats ? Entities::CommitWithStats : Entities::Commit
+ # This tells kaminari that there is 1 more commit after the one we've
+ # loaded, meaning there will be a next page, if the currently loaded set
+ # of commits is equal to the requested page size.
+ commit_count = offset + commits.size + 1
paginated_commits = Kaminari.paginate_array(commits, total_count: commit_count)
- serializer = with_stats ? Entities::CommitWithStats : Entities::Commit
-
- present paginate(paginated_commits), with: serializer
+ present paginate(paginated_commits, exclude_total_headers: true), with: serializer
end
desc 'Commit multiple file changes as one commit' do
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index 31d097c4bea..1becbd668a3 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -2,7 +2,7 @@
# PHP composer support (https://getcomposer.org/)
module API
- class ComposerPackages < Grape::API::Instance
+ class ComposerPackages < ::API::Base
helpers ::API::Helpers::PackagesManagerClientsHelpers
helpers ::API::Helpers::RelatedResourcesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
@@ -26,6 +26,10 @@ module API
render_api_error!(e.message, 400)
end
+ rescue_from Packages::Composer::ComposerJsonService::InvalidJson do |e|
+ render_api_error!(e.message, 422)
+ end
+
helpers do
def packages
strong_memoize(:packages) do
@@ -123,7 +127,7 @@ module API
bad_request!
end
- package_event('push_package')
+ track_package_event('push_package', :composer)
::Packages::Composer::CreatePackageService
.new(authorized_user_project, current_user, declared_params)
diff --git a/lib/api/conan_instance_packages.rb b/lib/api/conan_instance_packages.rb
index 209748d79fa..08265201328 100644
--- a/lib/api/conan_instance_packages.rb
+++ b/lib/api/conan_instance_packages.rb
@@ -2,7 +2,7 @@
# Conan Instance-Level Package Manager Client API
module API
- class ConanInstancePackages < Grape::API::Instance
+ class ConanInstancePackages < ::API::Base
namespace 'packages/conan/v1' do
include ConanPackageEndpoints
end
diff --git a/lib/api/conan_package_endpoints.rb b/lib/api/conan_package_endpoints.rb
index 445447cfcd2..9b6867a328b 100644
--- a/lib/api/conan_package_endpoints.rb
+++ b/lib/api/conan_package_endpoints.rb
@@ -246,7 +246,7 @@ module API
delete do
authorize!(:destroy_package, project)
- package_event('delete_package', category: 'API::ConanPackages')
+ track_package_event('delete_package', :conan, category: 'API::ConanPackages')
package.destroy
end
diff --git a/lib/api/conan_project_packages.rb b/lib/api/conan_project_packages.rb
index c51992231a7..db8cd187811 100644
--- a/lib/api/conan_project_packages.rb
+++ b/lib/api/conan_project_packages.rb
@@ -2,7 +2,7 @@
# Conan Project-Level Package Manager Client API
module API
- class ConanProjectPackages < Grape::API::Instance
+ class ConanProjectPackages < ::API::Base
params do
requires :id, type: Integer, desc: 'The ID of a project', regexp: %r{\A[1-9]\d*\z}
end
diff --git a/lib/api/container_registry_event.rb b/lib/api/container_registry_event.rb
index 0b7c35cadbd..6c4b80b612a 100644
--- a/lib/api/container_registry_event.rb
+++ b/lib/api/container_registry_event.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ContainerRegistryEvent < Grape::API::Instance
+ class ContainerRegistryEvent < ::API::Base
DOCKER_DISTRIBUTION_EVENTS_V1_JSON = 'application/vnd.docker.distribution.events.v1+json'
before { authenticate_registry_notification! }
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
new file mode 100644
index 00000000000..e3cacc4132f
--- /dev/null
+++ b/lib/api/debian_group_packages.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module API
+ class DebianGroupPackages < ::API::Base
+ params do
+ requires :id, type: String, desc: 'The ID of a group'
+ end
+
+ resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before do
+ not_found! unless ::Feature.enabled?(:debian_packages, user_group)
+
+ authorize_read_package!(user_group)
+ end
+
+ namespace ':id/-/packages/debian' do
+ include DebianPackageEndpoints
+ end
+ end
+ end
+end
diff --git a/lib/api/debian_package_endpoints.rb b/lib/api/debian_package_endpoints.rb
new file mode 100644
index 00000000000..168b3ca7a4f
--- /dev/null
+++ b/lib/api/debian_package_endpoints.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+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
+ }.freeze
+ COMPONENT_ARCHITECTURE_REQUIREMENTS = {
+ component: COMPONENT_REGEX,
+ architecture: ARCHITECTURE_REGEX
+ }.freeze
+ COMPONENT_LETTER_SOURCE_PACKAGE_REQUIREMENTS = {
+ component: COMPONENT_REGEX,
+ letter: LETTER_REGEX,
+ source_package: PACKAGE_REGEX
+ }.freeze
+ FILE_NAME_REQUIREMENTS = {
+ file_name: API::NO_SLASH_URL_PART_REGEX
+ }.freeze
+
+ included do
+ helpers ::API::Helpers::PackagesHelpers
+ helpers ::API::Helpers::Packages::BasicAuthHelpers
+
+ format :txt
+
+ rescue_from ArgumentError do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ rescue_from ActiveRecord::RecordInvalid do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ before do
+ require_packages_enabled!
+ end
+
+ params do
+ requires :distribution, type: String, desc: 'The Debian Codename', regexp: Gitlab::Regex.debian_distribution_regex
+ end
+
+ namespace 'dists/*distribution', requirements: DISTRIBUTION_REQUIREMENTS do
+ # GET {projects|groups}/:id/-/packages/debian/dists/*distribution/Release.gpg
+ desc 'The Release file signature' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ get 'Release.gpg' do
+ not_found!
+ end
+
+ # GET {projects|groups}/:id/-/packages/debian/dists/*distribution/Release
+ desc 'The unsigned Release file' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ get 'Release' do
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
+ 'TODO Release'
+ end
+
+ # GET {projects|groups}/:id/-/packages/debian/dists/*distribution/InRelease
+ desc 'The signed Release file' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ get 'InRelease' do
+ not_found!
+ end
+
+ params do
+ requires :component, type: String, desc: 'The Debian Component', regexp: Gitlab::Regex.debian_component_regex
+ requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
+ end
+
+ namespace ':component/binary-:architecture', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
+ # GET {projects|groups}/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages
+ desc 'The binary files index' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ get 'Packages' do
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
+ 'TODO Packages'
+ end
+ end
+ end
+
+ params do
+ requires :component, type: String, desc: 'The Debian Component', regexp: Gitlab::Regex.debian_component_regex
+ requires :letter, type: String, desc: 'The Debian Classification (first-letter or lib-first-letter)'
+ requires :source_package, type: String, desc: 'The Debian Source Package Name', regexp: Gitlab::Regex.debian_package_name_regex
+ end
+
+ namespace 'pool/:component/:letter/:source_package', requirements: COMPONENT_LETTER_SOURCE_PACKAGE_REQUIREMENTS do
+ # GET {projects|groups}/:id/-/packages/debian/pool/:component/:letter/:source_package/:file_name
+ params do
+ requires :file_name, type: String, desc: 'The Debian File Name'
+ end
+ desc 'The package' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ get ':file_name', requirements: FILE_NAME_REQUIREMENTS do
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
+ 'TODO File'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
new file mode 100644
index 00000000000..bcb4e8c8cbc
--- /dev/null
+++ b/lib/api/debian_project_packages.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module API
+ class DebianProjectPackages < ::API::Base
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before do
+ not_found! unless ::Feature.enabled?(:debian_packages, user_project)
+
+ authorize_read_package!
+ end
+
+ namespace ':id/-/packages/debian' do
+ include DebianPackageEndpoints
+
+ params do
+ requires :file_name, type: String, desc: 'The file name'
+ end
+
+ namespace 'incoming/:file_name', requirements: FILE_NAME_REQUIREMENTS do
+ # PUT {projects|groups}/:id/-/packages/debian/incoming/:file_name
+ params do
+ requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ put do
+ authorize_upload!(authorized_user_project)
+ bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:debian_max_file_size, params[:file].size)
+
+ track_package_event('push_package', :debian)
+
+ created!
+ rescue ObjectStorage::RemoteStoreError => e
+ Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
+
+ forbidden!
+ end
+
+ # PUT {projects|groups}/:id/-/packages/debian/incoming/:file_name/authorize
+ route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ post 'authorize' do
+ authorize_workhorse!(
+ subject: authorized_user_project,
+ has_length: false,
+ maximum_size: authorized_user_project.actual_limits.debian_max_file_size
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index ad37b7578ad..314f5b6ee1d 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class DeployKeys < Grape::API::Instance
+ class DeployKeys < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/deploy_tokens.rb b/lib/api/deploy_tokens.rb
index 96aa2445f56..1c156b8b3bb 100644
--- a/lib/api/deploy_tokens.rb
+++ b/lib/api/deploy_tokens.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class DeployTokens < Grape::API::Instance
+ class DeployTokens < ::API::Base
include PaginationParams
helpers do
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index 87144fd31cc..ff06bdbae16 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -2,7 +2,7 @@
module API
# Deployments RESTful API endpoints
- class Deployments < Grape::API::Instance
+ class Deployments < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index c431ec8e1e4..3d2608c8c5a 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Discussions < Grape::API::Instance
+ class Discussions < ::API::Base
include PaginationParams
helpers ::API::Helpers::NotesHelpers
helpers ::RendersNotes
diff --git a/lib/api/entities/ci/lint/result.rb b/lib/api/entities/ci/lint/result.rb
new file mode 100644
index 00000000000..0e4aa238ba2
--- /dev/null
+++ b/lib/api/entities/ci/lint/result.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module Lint
+ class Result < Grape::Entity
+ expose :valid?, as: :valid
+ expose :errors
+ expose :warnings
+ expose :merged_yaml
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/cluster.rb b/lib/api/entities/cluster.rb
index 4cb54e988ce..67459092a33 100644
--- a/lib/api/entities/cluster.rb
+++ b/lib/api/entities/cluster.rb
@@ -4,7 +4,7 @@ module API
module Entities
class Cluster < Grape::Entity
expose :id, :name, :created_at, :domain
- expose :provider_type, :platform_type, :environment_scope, :cluster_type
+ expose :provider_type, :platform_type, :environment_scope, :cluster_type, :namespace_per_environment
expose :user, using: Entities::UserBasic
expose :platform_kubernetes, using: Entities::Platform::Kubernetes
expose :provider_gcp, using: Entities::Provider::Gcp
diff --git a/lib/api/entities/container_registry.rb b/lib/api/entities/container_registry.rb
index cff627ab50a..c430b73580b 100644
--- a/lib/api/entities/container_registry.rb
+++ b/lib/api/entities/container_registry.rb
@@ -16,6 +16,7 @@ module API
expose :project_id
expose :location
expose :created_at
+ expose :expiration_policy_started_at, as: :cleanup_policy_started_at
expose :tags_count, if: -> (_, options) { options[:tags_count] }
expose :tags, using: Tag, if: -> (_, options) { options[:tags] }
end
diff --git a/lib/api/entities/feature_flag.rb b/lib/api/entities/feature_flag.rb
new file mode 100644
index 00000000000..82fdb20af00
--- /dev/null
+++ b/lib/api/entities/feature_flag.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class FeatureFlag < Grape::Entity
+ expose :name
+ expose :description
+ expose :active
+ expose :version, if: :feature_flags_new_version_enabled
+ expose :created_at
+ expose :updated_at
+ expose :scopes, using: FeatureFlag::LegacyScope
+ expose :strategies, using: FeatureFlag::Strategy, if: :feature_flags_new_version_enabled
+ end
+ end
+end
diff --git a/lib/api/entities/feature_flag/detailed_legacy_scope.rb b/lib/api/entities/feature_flag/detailed_legacy_scope.rb
new file mode 100644
index 00000000000..47078c1dfde
--- /dev/null
+++ b/lib/api/entities/feature_flag/detailed_legacy_scope.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class FeatureFlag < Grape::Entity
+ class DetailedLegacyScope < LegacyScope
+ expose :name
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/feature_flag/legacy_scope.rb b/lib/api/entities/feature_flag/legacy_scope.rb
new file mode 100644
index 00000000000..7329f71c599
--- /dev/null
+++ b/lib/api/entities/feature_flag/legacy_scope.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class FeatureFlag < Grape::Entity
+ class LegacyScope < Grape::Entity
+ expose :id
+ expose :active
+ expose :environment_scope
+ expose :strategies
+ expose :created_at
+ expose :updated_at
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/feature_flag/scope.rb b/lib/api/entities/feature_flag/scope.rb
new file mode 100644
index 00000000000..906fe718257
--- /dev/null
+++ b/lib/api/entities/feature_flag/scope.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class FeatureFlag < Grape::Entity
+ class Scope < Grape::Entity
+ expose :id
+ expose :environment_scope
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/feature_flag/strategy.rb b/lib/api/entities/feature_flag/strategy.rb
new file mode 100644
index 00000000000..32699be0ee3
--- /dev/null
+++ b/lib/api/entities/feature_flag/strategy.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class FeatureFlag < Grape::Entity
+ class Strategy < Grape::Entity
+ expose :id
+ expose :name
+ expose :parameters
+ expose :scopes, using: FeatureFlag::Scope
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/feature_flag/user_list.rb b/lib/api/entities/feature_flag/user_list.rb
new file mode 100644
index 00000000000..bc8b12ea22e
--- /dev/null
+++ b/lib/api/entities/feature_flag/user_list.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class FeatureFlag < Grape::Entity
+ class UserList < Grape::Entity
+ include RequestAwareEntity
+
+ expose :id
+ expose :iid
+ expose :project_id
+ expose :created_at
+ expose :updated_at
+ expose :name
+ expose :user_xids
+
+ expose :path do |list|
+ project_feature_flags_user_list_path(list.project, list)
+ end
+
+ expose :edit_path do |list|
+ edit_project_feature_flags_user_list_path(list.project, list)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/job_request/cache.rb b/lib/api/entities/job_request/cache.rb
index a75affbaf84..cd533d7e5b3 100644
--- a/lib/api/entities/job_request/cache.rb
+++ b/lib/api/entities/job_request/cache.rb
@@ -4,7 +4,7 @@ module API
module Entities
module JobRequest
class Cache < Grape::Entity
- expose :key, :untracked, :paths, :policy
+ expose :key, :untracked, :paths, :policy, :when
end
end
end
diff --git a/lib/api/entities/member.rb b/lib/api/entities/member.rb
index 14e97f41e77..ad62f92e5a0 100644
--- a/lib/api/entities/member.rb
+++ b/lib/api/entities/member.rb
@@ -5,6 +5,7 @@ module API
class Member < Grape::Entity
expose :user, merge: true, using: UserBasic
expose :access_level
+ expose :created_at
expose :expires_at
end
end
diff --git a/lib/api/entities/package.rb b/lib/api/entities/package.rb
index d903f50befa..b54f0e04a9d 100644
--- a/lib/api/entities/package.rb
+++ b/lib/api/entities/package.rb
@@ -7,7 +7,19 @@ module API
extend ::API::Entities::EntityHelpers
expose :id
- expose :name
+
+ expose :name do |package|
+ if package.conan?
+ package.conan_recipe
+ else
+ package.name
+ end
+ end
+
+ expose :conan_package_name, if: ->(package) { package.conan? } do |package|
+ package.name
+ end
+
expose :version
expose :package_type
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index fb599d68d72..82a44c75382 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -84,6 +84,7 @@ module API
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :ci_default_git_depth
+ expose :ci_forward_deployment_enabled
expose :public_builds, as: :public_jobs
expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
project.build_allow_git_fetch ? 'fetch' : 'clone'
diff --git a/lib/api/entities/snippet.rb b/lib/api/entities/snippet.rb
index 40488eb882d..85148c03d18 100644
--- a/lib/api/entities/snippet.rb
+++ b/lib/api/entities/snippet.rb
@@ -17,7 +17,7 @@ module API
expose :file_name do |snippet|
snippet.file_name_on_repo || snippet.file_name
end
- expose :files, if: ->(snippet, options) { snippet_multiple_files?(snippet, options[:current_user]) } do |snippet, options|
+ expose :files do |snippet, options|
snippet.list_files.map do |file|
{
path: file,
@@ -25,10 +25,6 @@ module API
}
end
end
-
- def snippet_multiple_files?(snippet, current_user)
- ::Feature.enabled?(:snippet_multiple_files, current_user) && snippet.repository_exists?
- end
end
end
end
diff --git a/lib/api/entities/unleash_feature.rb b/lib/api/entities/unleash_feature.rb
new file mode 100644
index 00000000000..8ee87d1fc11
--- /dev/null
+++ b/lib/api/entities/unleash_feature.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class UnleashFeature < Grape::Entity
+ expose :name
+ expose :description, unless: ->(feature) { feature.description.nil? }
+ expose :active, as: :enabled
+ expose :strategies do |flag|
+ flag.strategies.map do |strategy|
+ if legacy_strategy?(strategy)
+ UnleashLegacyStrategy.represent(strategy)
+ elsif gitlab_user_list_strategy?(strategy)
+ UnleashGitlabUserListStrategy.represent(strategy)
+ else
+ UnleashStrategy.represent(strategy)
+ end
+ end
+ end
+
+ private
+
+ def legacy_strategy?(strategy)
+ !strategy.respond_to?(:name)
+ end
+
+ def gitlab_user_list_strategy?(strategy)
+ strategy.name == ::Operations::FeatureFlags::Strategy::STRATEGY_GITLABUSERLIST
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/unleash_gitlab_user_list_strategy.rb b/lib/api/entities/unleash_gitlab_user_list_strategy.rb
new file mode 100644
index 00000000000..5617f8002d9
--- /dev/null
+++ b/lib/api/entities/unleash_gitlab_user_list_strategy.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class UnleashGitlabUserListStrategy < Grape::Entity
+ expose :name do |_strategy|
+ ::Operations::FeatureFlags::Strategy::STRATEGY_USERWITHID
+ end
+ expose :parameters do |strategy|
+ { userIds: strategy.user_list.user_xids }
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/unleash_legacy_strategy.rb b/lib/api/entities/unleash_legacy_strategy.rb
new file mode 100644
index 00000000000..5d5954f8da0
--- /dev/null
+++ b/lib/api/entities/unleash_legacy_strategy.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class UnleashLegacyStrategy < Grape::Entity
+ expose :name do |strategy|
+ strategy['name']
+ end
+ expose :parameters do |strategy|
+ strategy['parameters']
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/unleash_strategy.rb b/lib/api/entities/unleash_strategy.rb
new file mode 100644
index 00000000000..7627ce3873c
--- /dev/null
+++ b/lib/api/entities/unleash_strategy.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class UnleashStrategy < Grape::Entity
+ expose :name
+ expose :parameters
+ end
+ end
+end
diff --git a/lib/api/entities/user_with_admin.rb b/lib/api/entities/user_with_admin.rb
index c225ade6eb6..ab7bc738ff8 100644
--- a/lib/api/entities/user_with_admin.rb
+++ b/lib/api/entities/user_with_admin.rb
@@ -8,3 +8,5 @@ module API
end
end
end
+
+API::Entities::UserWithAdmin.prepend_if_ee('EE::API::Entities::UserWithAdmin')
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index b825904e2c5..0e780d4ef36 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -2,7 +2,7 @@
module API
# Environments RESTfull API endpoints
- class Environments < Grape::API::Instance
+ class Environments < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/error_tracking.rb b/lib/api/error_tracking.rb
index 64ec6f0a57a..03f83477954 100644
--- a/lib/api/error_tracking.rb
+++ b/lib/api/error_tracking.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ErrorTracking < Grape::API::Instance
+ class ErrorTracking < ::API::Base
before { authenticate! }
params do
diff --git a/lib/api/events.rb b/lib/api/events.rb
index 0b79431a76d..43efacf9c0b 100644
--- a/lib/api/events.rb
+++ b/lib/api/events.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Events < Grape::API::Instance
+ class Events < ::API::Base
include PaginationParams
include APIGuard
helpers ::API::Helpers::EventsHelpers
diff --git a/lib/api/feature_flag_scopes.rb b/lib/api/feature_flag_scopes.rb
new file mode 100644
index 00000000000..d77e243aa88
--- /dev/null
+++ b/lib/api/feature_flag_scopes.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+module API
+ class FeatureFlagScopes < ::API::Base
+ include PaginationParams
+
+ ENVIRONMENT_SCOPE_ENDPOINT_REQUIREMENTS = FeatureFlags::FEATURE_FLAG_ENDPOINT_REQUIREMENTS
+ .merge(environment_scope: API::NO_SLASH_URL_PART_REGEX)
+
+ before do
+ authorize_read_feature_flags!
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ resource :feature_flag_scopes do
+ desc 'Get all effective feature flags under the environment' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag::DetailedLegacyScope
+ end
+ params do
+ requires :environment, type: String, desc: 'The environment name'
+ end
+ get do
+ present scopes_for_environment, with: ::API::Entities::FeatureFlag::DetailedLegacyScope
+ end
+ end
+
+ params do
+ requires :name, type: String, desc: 'The name of the feature flag'
+ end
+ resource 'feature_flags/:name', requirements: FeatureFlags::FEATURE_FLAG_ENDPOINT_REQUIREMENTS do
+ resource :scopes do
+ desc 'Get all scopes of a feature flag' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag::LegacyScope
+ end
+ params do
+ use :pagination
+ end
+ get do
+ present paginate(feature_flag.scopes), with: ::API::Entities::FeatureFlag::LegacyScope
+ end
+
+ desc 'Create a scope of a feature flag' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag::LegacyScope
+ end
+ params do
+ requires :environment_scope, type: String, desc: 'The environment scope of the scope'
+ requires :active, type: Boolean, desc: 'Whether the scope is active'
+ requires :strategies, type: JSON, desc: 'The strategies of the scope'
+ end
+ post do
+ authorize_update_feature_flag!
+
+ result = ::FeatureFlags::UpdateService
+ .new(user_project, current_user, scopes_attributes: [declared_params])
+ .execute(feature_flag)
+
+ if result[:status] == :success
+ present scope, with: ::API::Entities::FeatureFlag::LegacyScope
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
+ params do
+ requires :environment_scope, type: String, desc: 'URL-encoded environment scope'
+ end
+ resource ':environment_scope', requirements: ENVIRONMENT_SCOPE_ENDPOINT_REQUIREMENTS do
+ desc 'Get a scope of a feature flag' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag::LegacyScope
+ end
+ get do
+ present scope, with: ::API::Entities::FeatureFlag::LegacyScope
+ end
+
+ desc 'Update a scope of a feature flag' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag::LegacyScope
+ end
+ params do
+ optional :active, type: Boolean, desc: 'Whether the scope is active'
+ optional :strategies, type: JSON, desc: 'The strategies of the scope'
+ end
+ put do
+ authorize_update_feature_flag!
+
+ scope_attributes = declared_params.merge(id: scope.id)
+
+ result = ::FeatureFlags::UpdateService
+ .new(user_project, current_user, scopes_attributes: [scope_attributes])
+ .execute(feature_flag)
+
+ if result[:status] == :success
+ updated_scope = result[:feature_flag].scopes
+ .find { |scope| scope.environment_scope == params[:environment_scope] }
+
+ present updated_scope, with: ::API::Entities::FeatureFlag::LegacyScope
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
+ desc 'Delete a scope from a feature flag' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag::LegacyScope
+ end
+ delete do
+ authorize_update_feature_flag!
+
+ param = { scopes_attributes: [{ id: scope.id, _destroy: true }] }
+
+ result = ::FeatureFlags::UpdateService
+ .new(user_project, current_user, param)
+ .execute(feature_flag)
+
+ if result[:status] == :success
+ status :no_content
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+ end
+ end
+ end
+ end
+
+ helpers do
+ def authorize_read_feature_flags!
+ authorize! :read_feature_flag, user_project
+ end
+
+ def authorize_update_feature_flag!
+ authorize! :update_feature_flag, feature_flag
+ end
+
+ def feature_flag
+ @feature_flag ||= user_project.operations_feature_flags
+ .find_by_name!(params[:name])
+ end
+
+ def scope
+ @scope ||= feature_flag.scopes
+ .find_by_environment_scope!(CGI.unescape(params[:environment_scope]))
+ end
+
+ def scopes_for_environment
+ Operations::FeatureFlagScope
+ .for_unleash_client(user_project, params[:environment])
+ end
+ end
+ end
+end
diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb
new file mode 100644
index 00000000000..613c3fb0f5b
--- /dev/null
+++ b/lib/api/feature_flags.rb
@@ -0,0 +1,266 @@
+# frozen_string_literal: true
+
+module API
+ class FeatureFlags < ::API::Base
+ include PaginationParams
+
+ FEATURE_FLAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
+ .merge(name: API::NO_SLASH_URL_PART_REGEX)
+
+ before do
+ authorize_read_feature_flags!
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ resource :feature_flags do
+ desc 'Get all feature flags of a project' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag
+ end
+ params do
+ optional :scope, type: String, desc: 'The scope of feature flags',
+ values: %w[enabled disabled]
+ use :pagination
+ end
+ get do
+ feature_flags = ::FeatureFlagsFinder
+ .new(user_project, current_user, declared_params(include_missing: false))
+ .execute
+
+ present_entity(paginate(feature_flags))
+ end
+
+ desc 'Create a new feature flag' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag
+ end
+ params do
+ requires :name, type: String, desc: 'The name of feature flag'
+ optional :description, type: String, desc: 'The description of the feature flag'
+ optional :active, type: Boolean, desc: 'Active/inactive value of the flag'
+ optional :version, type: String, desc: 'The version of the feature flag'
+ optional :scopes, type: Array do
+ requires :environment_scope, type: String, desc: 'The environment scope of the scope'
+ requires :active, type: Boolean, desc: 'Active/inactive of the scope'
+ requires :strategies, type: JSON, desc: 'The strategies of the scope'
+ end
+ optional :strategies, type: Array do
+ requires :name, type: String, desc: 'The strategy name'
+ requires :parameters, type: JSON, desc: 'The strategy parameters'
+ optional :scopes, type: Array do
+ requires :environment_scope, type: String, desc: 'The environment scope of the scope'
+ end
+ end
+ end
+ post do
+ authorize_create_feature_flag!
+
+ attrs = declared_params(include_missing: false)
+
+ ensure_post_version_2_flags_enabled! if attrs[:version] == 'new_version_flag'
+
+ rename_key(attrs, :scopes, :scopes_attributes)
+ rename_key(attrs, :strategies, :strategies_attributes)
+ update_value(attrs, :strategies_attributes) do |strategies|
+ strategies.map { |s| rename_key(s, :scopes, :scopes_attributes) }
+ end
+
+ result = ::FeatureFlags::CreateService
+ .new(user_project, current_user, attrs)
+ .execute
+
+ if result[:status] == :success
+ present_entity(result[:feature_flag])
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+ end
+
+ params do
+ requires :feature_flag_name, type: String, desc: 'The name of the feature flag'
+ end
+ resource 'feature_flags/:feature_flag_name', requirements: FEATURE_FLAG_ENDPOINT_REQUIREMENTS do
+ desc 'Get a feature flag of a project' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag
+ end
+ get do
+ authorize_read_feature_flag!
+
+ present_entity(feature_flag)
+ end
+
+ desc 'Enable a strategy for a feature flag on an environment' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag
+ end
+ params do
+ requires :environment_scope, type: String, desc: 'The environment scope of the feature flag'
+ requires :strategy, type: JSON, desc: 'The strategy to be enabled on the scope'
+ end
+ post :enable do
+ not_found! unless Feature.enabled?(:feature_flag_api, user_project)
+ render_api_error!('Version 2 flags not supported', :unprocessable_entity) if new_version_flag_present?
+
+ result = ::FeatureFlags::EnableService
+ .new(user_project, current_user, params).execute
+
+ if result[:status] == :success
+ status :ok
+ present_entity(result[:feature_flag])
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
+ desc 'Disable a strategy for a feature flag on an environment' do
+ detail 'This feature is going to be introduced in GitLab 12.5 if `feature_flag_api` feature flag is removed'
+ success ::API::Entities::FeatureFlag
+ end
+ params do
+ requires :environment_scope, type: String, desc: 'The environment scope of the feature flag'
+ requires :strategy, type: JSON, desc: 'The strategy to be disabled on the scope'
+ end
+ post :disable do
+ not_found! unless Feature.enabled?(:feature_flag_api, user_project)
+ render_api_error!('Version 2 flags not supported', :unprocessable_entity) if feature_flag.new_version_flag?
+
+ result = ::FeatureFlags::DisableService
+ .new(user_project, current_user, params).execute
+
+ if result[:status] == :success
+ status :ok
+ present_entity(result[:feature_flag])
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
+ desc 'Update a feature flag' do
+ detail 'This feature will be introduced in GitLab 13.1 if feature_flags_new_version feature flag is removed'
+ success ::API::Entities::FeatureFlag
+ end
+ params do
+ optional :name, type: String, desc: 'The name of the feature flag'
+ optional :description, type: String, desc: 'The description of the feature flag'
+ optional :active, type: Boolean, desc: 'Active/inactive value of the flag'
+ optional :strategies, type: Array do
+ optional :id, type: Integer, desc: 'The strategy id'
+ optional :name, type: String, desc: 'The strategy type'
+ optional :parameters, type: JSON, desc: 'The strategy parameters'
+ optional :_destroy, type: Boolean, desc: 'Delete the strategy when true'
+ optional :scopes, type: Array do
+ optional :id, type: Integer, desc: 'The environment scope id'
+ optional :environment_scope, type: String, desc: 'The environment scope of the scope'
+ optional :_destroy, type: Boolean, desc: 'Delete the scope when true'
+ end
+ end
+ end
+ put do
+ not_found! unless feature_flags_new_version_enabled?
+ authorize_update_feature_flag!
+ render_api_error!('PUT operations are not supported for legacy feature flags', :unprocessable_entity) if feature_flag.legacy_flag?
+
+ attrs = declared_params(include_missing: false)
+
+ rename_key(attrs, :strategies, :strategies_attributes)
+ update_value(attrs, :strategies_attributes) do |strategies|
+ strategies.map { |s| rename_key(s, :scopes, :scopes_attributes) }
+ end
+
+ result = ::FeatureFlags::UpdateService
+ .new(user_project, current_user, attrs)
+ .execute(feature_flag)
+
+ if result[:status] == :success
+ present_entity(result[:feature_flag])
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
+ desc 'Delete a feature flag' do
+ detail 'This feature was introduced in GitLab 12.5'
+ success ::API::Entities::FeatureFlag
+ end
+ delete do
+ authorize_destroy_feature_flag!
+
+ result = ::FeatureFlags::DestroyService
+ .new(user_project, current_user, declared_params(include_missing: false))
+ .execute(feature_flag)
+
+ if result[:status] == :success
+ present_entity(result[:feature_flag])
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+ end
+ end
+
+ helpers do
+ def authorize_read_feature_flags!
+ authorize! :read_feature_flag, user_project
+ end
+
+ def authorize_read_feature_flag!
+ authorize! :read_feature_flag, feature_flag
+ end
+
+ def authorize_create_feature_flag!
+ authorize! :create_feature_flag, user_project
+ end
+
+ def authorize_update_feature_flag!
+ authorize! :update_feature_flag, feature_flag
+ end
+
+ def authorize_destroy_feature_flag!
+ authorize! :destroy_feature_flag, feature_flag
+ end
+
+ def present_entity(result)
+ present result,
+ with: ::API::Entities::FeatureFlag,
+ feature_flags_new_version_enabled: feature_flags_new_version_enabled?
+ end
+
+ def ensure_post_version_2_flags_enabled!
+ unless feature_flags_new_version_enabled?
+ render_api_error!('Version 2 flags are not enabled for this project', :unprocessable_entity)
+ end
+ end
+
+ def feature_flag
+ @feature_flag ||= if feature_flags_new_version_enabled?
+ user_project.operations_feature_flags.find_by_name!(params[:feature_flag_name])
+ else
+ user_project.operations_feature_flags.legacy_flag.find_by_name!(params[:feature_flag_name])
+ end
+ end
+
+ def new_version_flag_present?
+ user_project.operations_feature_flags.new_version_flag.find_by_name(params[:name]).present?
+ end
+
+ def feature_flags_new_version_enabled?
+ Feature.enabled?(:feature_flags_new_version, user_project, default_enabled: true)
+ end
+
+ def rename_key(hash, old_key, new_key)
+ hash[new_key] = hash.delete(old_key) if hash.key?(old_key)
+ hash
+ end
+
+ def update_value(hash, key)
+ hash[key] = yield(hash[key]) if hash.key?(key)
+ hash
+ end
+ end
+ end
+end
diff --git a/lib/api/feature_flags_user_lists.rb b/lib/api/feature_flags_user_lists.rb
new file mode 100644
index 00000000000..e5218cfd7f1
--- /dev/null
+++ b/lib/api/feature_flags_user_lists.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+module API
+ class FeatureFlagsUserLists < ::API::Base
+ include PaginationParams
+
+ error_formatter :json, -> (message, _backtrace, _options, _env, _original_exception) {
+ message.is_a?(String) ? { message: message }.to_json : message.to_json
+ }
+
+ before do
+ authorize_admin_feature_flags_user_lists!
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ resource :feature_flags_user_lists do
+ desc 'Get all feature flags user lists of a project' do
+ detail 'This feature was introduced in GitLab 12.10'
+ success ::API::Entities::FeatureFlag::UserList
+ end
+ params do
+ use :pagination
+ end
+ get do
+ present paginate(user_project.operations_feature_flags_user_lists),
+ with: ::API::Entities::FeatureFlag::UserList
+ end
+
+ desc 'Create a feature flags user list for a project' do
+ detail 'This feature was introduced in GitLab 12.10'
+ success ::API::Entities::FeatureFlag::UserList
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the list'
+ requires :user_xids, type: String, desc: 'A comma separated list of external user ids'
+ end
+ post do
+ list = user_project.operations_feature_flags_user_lists.create(declared_params)
+
+ if list.save
+ present list, with: ::API::Entities::FeatureFlag::UserList
+ else
+ render_api_error!(list.errors.full_messages, :bad_request)
+ end
+ end
+ end
+
+ params do
+ requires :iid, type: String, desc: 'The internal id of the user list'
+ end
+ resource 'feature_flags_user_lists/:iid' do
+ desc 'Get a single feature flag user list belonging to a project' do
+ detail 'This feature was introduced in GitLab 12.10'
+ success ::API::Entities::FeatureFlag::UserList
+ end
+ get do
+ present user_project.operations_feature_flags_user_lists.find_by_iid!(params[:iid]),
+ with: ::API::Entities::FeatureFlag::UserList
+ end
+
+ desc 'Update a feature flag user list' do
+ detail 'This feature was introduced in GitLab 12.10'
+ success ::API::Entities::FeatureFlag::UserList
+ end
+ params do
+ optional :name, type: String, desc: 'The name of the list'
+ optional :user_xids, type: String, desc: 'A comma separated list of external user ids'
+ end
+ put do
+ list = user_project.operations_feature_flags_user_lists.find_by_iid!(params[:iid])
+
+ if list.update(declared_params(include_missing: false))
+ present list, with: ::API::Entities::FeatureFlag::UserList
+ else
+ render_api_error!(list.errors.full_messages, :bad_request)
+ end
+ end
+
+ desc 'Delete a feature flag user list' do
+ detail 'This feature was introduced in GitLab 12.10'
+ end
+ delete do
+ list = user_project.operations_feature_flags_user_lists.find_by_iid!(params[:iid])
+ unless list.destroy
+ render_api_error!(list.errors.full_messages, :conflict)
+ end
+ end
+ end
+ end
+
+ helpers do
+ def authorize_admin_feature_flags_user_lists!
+ authorize! :admin_feature_flags_user_lists, user_project
+ end
+ end
+ end
+end
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 9d011d658f6..5d2e545abd6 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Features < Grape::API::Instance
+ class Features < ::API::Base
before { authenticated_as_admin! }
helpers do
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 748bdfa894d..6833fc429e2 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Files < Grape::API::Instance
+ class Files < ::API::Base
include APIGuard
FILE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(file_path: API::NO_SLASH_URL_PART_REGEX)
diff --git a/lib/api/freeze_periods.rb b/lib/api/freeze_periods.rb
index b8254ee9ab4..a83e36165a2 100644
--- a/lib/api/freeze_periods.rb
+++ b/lib/api/freeze_periods.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class FreezePeriods < Grape::API::Instance
+ class FreezePeriods < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 98b8a40c7c9..a0c33ab65b9 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -1,7 +1,12 @@
# frozen_string_literal: true
module API
- class GenericPackages < Grape::API::Instance
+ class GenericPackages < ::API::Base
+ GENERIC_PACKAGES_REQUIREMENTS = {
+ package_name: API::NO_SLASH_URL_PART_REGEX,
+ file_name: API::NO_SLASH_URL_PART_REGEX
+ }.freeze
+
before do
require_packages_enabled!
authenticate!
@@ -17,17 +22,94 @@ module API
route_setting :authentication, job_token_allowed: true
namespace ':id/packages/generic' do
- get 'ping' do
- :pong
+ namespace ':package_name/*package_version/:file_name', requirements: GENERIC_PACKAGES_REQUIREMENTS do
+ desc 'Workhorse authorize generic package file' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true
+
+ params do
+ requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.generic_package_name_regex, file_path: true
+ requires :package_version, type: String, desc: 'Package version', regexp: Gitlab::Regex.generic_package_version_regex
+ requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.generic_package_file_name_regex, file_path: true
+ end
+
+ put 'authorize' do
+ authorize_workhorse!(subject: project, maximum_size: project.actual_limits.generic_packages_max_file_size)
+ end
+
+ desc 'Upload package file' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ params do
+ requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.generic_package_name_regex, file_path: true
+ requires :package_version, type: String, desc: 'Package version', regexp: Gitlab::Regex.generic_package_version_regex
+ requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.generic_package_file_name_regex, file_path: true
+ requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, job_token_allowed: true
+
+ put do
+ authorize_upload!(project)
+ bad_request!('File is too large') if max_file_size_exceeded?
+
+ track_event('push_package')
+
+ create_package_file_params = declared_params.merge(build: current_authenticated_job)
+ ::Packages::Generic::CreatePackageFileService
+ .new(project, current_user, create_package_file_params)
+ .execute
+
+ created!
+ rescue ObjectStorage::RemoteStoreError => e
+ Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project.id })
+
+ forbidden!
+ end
+
+ desc 'Download package file' do
+ detail 'This feature was introduced in GitLab 13.5'
+ end
+
+ params do
+ requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.generic_package_name_regex, file_path: true
+ requires :package_version, type: String, desc: 'Package version', regexp: Gitlab::Regex.generic_package_version_regex
+ requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.generic_package_file_name_regex, file_path: true
+ end
+
+ route_setting :authentication, job_token_allowed: true
+
+ get do
+ authorize_read_package!(project)
+
+ package = ::Packages::Generic::PackageFinder.new(project).execute!(params[:package_name], params[:package_version])
+ package_file = ::Packages::PackageFileFinder.new(package, params[:file_name]).execute!
+
+ track_event('pull_package')
+
+ present_carrierwave_file!(package_file.file)
+ end
end
end
end
helpers do
include ::API::Helpers::PackagesHelpers
+ include ::API::Helpers::Packages::BasicAuthHelpers
def require_generic_packages_available!
- not_found! unless Feature.enabled?(:generic_packages, user_project)
+ not_found! unless Feature.enabled?(:generic_packages, project, default_enabled: true)
+ end
+
+ def project
+ authorized_user_project
+ end
+
+ def max_file_size_exceeded?
+ project.actual_limits.exceeded?(:generic_packages_max_file_size, params[:file].size)
end
end
end
diff --git a/lib/api/github/entities.rb b/lib/api/github/entities.rb
index c28a0b8eb7e..fe228c9a2d2 100644
--- a/lib/api/github/entities.rb
+++ b/lib/api/github/entities.rb
@@ -119,7 +119,9 @@ module API
expose :username, as: :login
expose :user_url, as: :url
expose :user_url, as: :html_url
- expose :avatar_url
+ expose :avatar_url do |user|
+ user.avatar_url(only_path: false)
+ end
private
diff --git a/lib/api/go_proxy.rb b/lib/api/go_proxy.rb
index c0207f9169c..30f0cfb4dfd 100755
--- a/lib/api/go_proxy.rb
+++ b/lib/api/go_proxy.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
module API
- class GoProxy < Grape::API::Instance
+ class GoProxy < ::API::Base
helpers Gitlab::Golang
helpers ::API::Helpers::PackagesHelpers
diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb
index 7efc12121d2..d4574b22d99 100644
--- a/lib/api/group_boards.rb
+++ b/lib/api/group_boards.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupBoards < Grape::API::Instance
+ class GroupBoards < ::API::Base
include BoardsResponses
include PaginationParams
diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb
index ae41d9f13b8..75429cf7a5c 100644
--- a/lib/api/group_clusters.rb
+++ b/lib/api/group_clusters.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupClusters < Grape::API::Instance
+ class GroupClusters < ::API::Base
include PaginationParams
before { authenticate! }
@@ -41,6 +41,7 @@ module API
requires :name, type: String, desc: 'Cluster name'
optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true'
optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster'
+ optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :domain, type: String, desc: 'Cluster base domain'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true'
@@ -74,6 +75,7 @@ module API
optional :name, type: String, desc: 'Cluster name'
optional :domain, type: String, desc: 'Cluster base domain'
optional :environment_scope, type: String, desc: 'The associated environment to the cluster'
+ optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
diff --git a/lib/api/group_container_repositories.rb b/lib/api/group_container_repositories.rb
index 25b3059f63b..1bb26b3931c 100644
--- a/lib/api/group_container_repositories.rb
+++ b/lib/api/group_container_repositories.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
module API
- class GroupContainerRepositories < Grape::API::Instance
+ class GroupContainerRepositories < ::API::Base
include PaginationParams
+ helpers ::API::Helpers::PackagesHelpers
+
before { authorize_read_group_container_images! }
REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
@@ -27,7 +29,7 @@ module API
user: current_user, subject: user_group
).execute
- track_event('list_repositories')
+ track_package_event('list_repositories', :container)
present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count]
end
diff --git a/lib/api/group_export.rb b/lib/api/group_export.rb
index dc14813eefc..6ebaa8de185 100644
--- a/lib/api/group_export.rb
+++ b/lib/api/group_export.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupExport < Grape::API::Instance
+ class GroupExport < ::API::Base
helpers Helpers::RateLimiter
before do
diff --git a/lib/api/group_import.rb b/lib/api/group_import.rb
index b82d9fc519a..e703a217fd5 100644
--- a/lib/api/group_import.rb
+++ b/lib/api/group_import.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupImport < Grape::API::Instance
+ class GroupImport < ::API::Base
helpers Helpers::FileUploadHelpers
helpers do
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index 56f2b769464..8443ddf10ce 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupLabels < Grape::API::Instance
+ class GroupLabels < ::API::Base
include PaginationParams
helpers ::API::Helpers::LabelHelpers
diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb
index 82f5df79356..aef9877b84c 100644
--- a/lib/api/group_milestones.rb
+++ b/lib/api/group_milestones.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupMilestones < Grape::API::Instance
+ class GroupMilestones < ::API::Base
include MilestoneResponses
include PaginationParams
diff --git a/lib/api/group_packages.rb b/lib/api/group_packages.rb
index aa047e260f5..5b6290df0dd 100644
--- a/lib/api/group_packages.rb
+++ b/lib/api/group_packages.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupPackages < Grape::API::Instance
+ class GroupPackages < ::API::Base
include PaginationParams
before do
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index e7b8cd10197..ee110d67fa5 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class GroupVariables < Grape::API::Instance
+ class GroupVariables < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 813e41b4d39..bf3d6c3c7e0 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Groups < Grape::API::Instance
+ class Groups < ::API::Base
include PaginationParams
include Helpers::CustomAttributes
@@ -29,7 +29,12 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def find_groups(params, parent_id = nil)
- find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
+ find_params = params.slice(
+ :all_available,
+ :custom_attributes,
+ :owned, :min_access_level,
+ :include_parent_descendants
+ )
find_params[:parent] = if params[:top_level_only]
[nil]
@@ -309,6 +314,19 @@ module API
present_groups params, groups
end
+ desc 'Get a list of descendant groups of this group.' do
+ success Entities::Group
+ end
+ params do
+ use :group_list_params
+ use :with_custom_attributes
+ end
+ get ":id/descendant_groups" do
+ finder_params = declared_params(include_missing: false).merge(include_parent_descendants: true)
+ groups = find_groups(finder_params, params[:id])
+ present_groups params, groups
+ end
+
desc 'Transfer a project to the group namespace. Available only for admin.' do
success Entities::GroupDetail
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 1912a06682e..c8aee1f3479 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -522,7 +522,7 @@ module API
else
header(*Gitlab::Workhorse.send_url(file.url))
status :ok
- body
+ body ""
end
end
@@ -544,7 +544,6 @@ module API
feature_name = "usage_data_#{event_name}"
return unless Feature.enabled?(feature_name)
- return unless Gitlab::CurrentSettings.usage_ping_enabled?
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(values, event_name)
rescue => error
diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb
index f3dfc093926..ba07a70ee32 100644
--- a/lib/api/helpers/groups_helpers.rb
+++ b/lib/api/helpers/groups_helpers.rb
@@ -24,6 +24,7 @@ module API
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master'
+ optional :shared_runners_setting, type: String, values: ::Namespace::SHARED_RUNNERS_SETTINGS, desc: 'Enable/disable shared runners for the group and its subgroups and projects'
end
params :optional_params_ee do
diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb
index e4163c63575..9b38eeb1e72 100644
--- a/lib/api/helpers/merge_requests_helpers.rb
+++ b/lib/api/helpers/merge_requests_helpers.rb
@@ -73,6 +73,13 @@ module API
optional :not, type: Hash, desc: 'Parameters to negate' do
use :merge_requests_negatable_params
end
+
+ optional :deployed_before,
+ 'Return merge requests deployed before the given date/time'
+ optional :deployed_after,
+ 'Return merge requests deployed after the given date/time'
+ optional :environment,
+ 'Returns merge requests deployed to the given environment'
end
params :optional_scope_param do
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index dcbf933a4e1..934e18bdd0a 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -158,7 +158,7 @@ module API
conan_package_reference: params[:conan_package_reference]
).execute!
- package_event('pull_package', category: 'API::ConanPackages') if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY
+ track_package_event('pull_package', :conan, category: 'API::ConanPackages') if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY
present_carrierwave_file!(package_file.file)
end
@@ -169,7 +169,7 @@ module API
def track_push_package_event
if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY && params[:file].size > 0 # rubocop: disable Style/ZeroLengthPredicate
- package_event('push_package', category: 'API::ConanPackages')
+ track_package_event('push_package', :conan, category: 'API::ConanPackages')
end
end
diff --git a/lib/api/helpers/packages/dependency_proxy_helpers.rb b/lib/api/helpers/packages/dependency_proxy_helpers.rb
index 254af7690a2..577ba97d68a 100644
--- a/lib/api/helpers/packages/dependency_proxy_helpers.rb
+++ b/lib/api/helpers/packages/dependency_proxy_helpers.rb
@@ -10,6 +10,7 @@ module API
def redirect_registry_request(forward_to_registry, package_type, options)
if forward_to_registry && redirect_registry_request_available?
+ track_event("#{package_type}_request_forward")
redirect(registry_url(package_type, options))
else
yield
diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb
index 403f5ea3851..e1898d28ef7 100644
--- a/lib/api/helpers/packages_helpers.rb
+++ b/lib/api/helpers/packages_helpers.rb
@@ -40,7 +40,7 @@ module API
params = { has_length: has_length }
params[:maximum_size] = maximum_size unless has_length
- ::Packages::PackageFileUploader.workhorse_authorize(params)
+ ::Packages::PackageFileUploader.workhorse_authorize(**params)
end
def authorize_upload!(subject = user_project)
@@ -48,7 +48,8 @@ module API
require_gitlab_workhorse!
end
- def package_event(event_name, **args)
+ def track_package_event(event_name, scope, **args)
+ ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute
track_event(event_name, **args)
end
end
diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb
index a6ae9a87f98..227aec224e5 100644
--- a/lib/api/helpers/pagination.rb
+++ b/lib/api/helpers/pagination.rb
@@ -3,8 +3,8 @@
module API
module Helpers
module Pagination
- def paginate(relation)
- Gitlab::Pagination::OffsetPagination.new(self).paginate(relation)
+ def paginate(*args)
+ Gitlab::Pagination::OffsetPagination.new(self).paginate(*args)
end
end
end
diff --git a/lib/api/helpers/presentable.rb b/lib/api/helpers/presentable.rb
index a5186cc56ea..40e1b266df5 100644
--- a/lib/api/helpers/presentable.rb
+++ b/lib/api/helpers/presentable.rb
@@ -23,7 +23,7 @@ module API
def initialize(object, options = {})
options = options.opts_hash if options.is_a?(Grape::Entity::Options)
- super(object.present(options), options)
+ super(object.present(**options), options)
end
end
end
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 8c20f5b8fc2..0364ba2ad9e 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -83,9 +83,18 @@ module API
params :optional_filter_params_ee do
end
+ params :optional_update_params_ce do
+ optional :ci_forward_deployment_enabled, type: Boolean, desc: 'Skip older deployment jobs that are still pending'
+ end
+
params :optional_update_params_ee do
end
+ params :optional_update_params do
+ use :optional_update_params_ce
+ use :optional_update_params_ee
+ end
+
params :optional_container_expiration_policy_params do
optional :cadence, type: String, desc: 'Container expiration policy cadence for recurring job'
optional :keep_n, type: String, desc: 'Container expiration policy number of images to keep'
@@ -108,6 +117,7 @@ module API
:builds_access_level,
:ci_config_path,
:ci_default_git_depth,
+ :ci_forward_deployment_enabled,
:container_registry_enabled,
:container_expiration_policy_attributes,
:default_branch,
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 34a2fb09875..1c85669a626 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -51,9 +51,7 @@ module API
job_forbidden!(job, 'Job is not running') unless job.running?
end
- if Gitlab::Ci::Features.job_heartbeats_runner?(job.project)
- job.runner&.heartbeat(get_runner_ip)
- end
+ job.runner&.heartbeat(get_runner_ip)
job
end
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 4bceda51900..4adb27a7414 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -381,6 +381,12 @@ module API
type: String,
desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
},
+ {
+ required: false,
+ name: :branches_to_be_notified,
+ type: String,
+ desc: 'Branches for which notifications are to be sent'
+ },
chat_notification_events
].flatten,
'hipchat' => [
diff --git a/lib/api/helpers/settings_helpers.rb b/lib/api/helpers/settings_helpers.rb
index 65aec6ae2e7..451e578fdd6 100644
--- a/lib/api/helpers/settings_helpers.rb
+++ b/lib/api/helpers/settings_helpers.rb
@@ -12,6 +12,7 @@ module API
def self.optional_attributes
[*::ApplicationSettingsHelper.visible_attributes,
*::ApplicationSettingsHelper.external_authorization_service_attributes,
+ *::ApplicationSettingsHelper.deprecated_attributes,
:performance_bar_allowed_group_id].freeze
end
end
diff --git a/lib/api/helpers/snippets_helpers.rb b/lib/api/helpers/snippets_helpers.rb
index 9224381735f..42f56680ded 100644
--- a/lib/api/helpers/snippets_helpers.rb
+++ b/lib/api/helpers/snippets_helpers.rb
@@ -93,7 +93,7 @@ module API
def validate_params_for_multiple_files(snippet)
return unless params[:content] || params[:file_name]
- if Feature.enabled?(:snippet_multiple_files, current_user) && snippet.multiple_files?
+ if snippet.multiple_files?
render_api_error!({ error: _('To update Snippets with multiple files, you must use the `files` parameter') }, 400)
end
end
diff --git a/lib/api/import_bitbucket_server.rb b/lib/api/import_bitbucket_server.rb
index df3235420e9..a0238c24f3b 100644
--- a/lib/api/import_bitbucket_server.rb
+++ b/lib/api/import_bitbucket_server.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ImportBitbucketServer < Grape::API::Instance
+ class ImportBitbucketServer < ::API::Base
helpers do
def client
@client ||= BitbucketServer::Client.new(credentials)
diff --git a/lib/api/import_github.rb b/lib/api/import_github.rb
index 0bab891eada..61fce7a2c1b 100644
--- a/lib/api/import_github.rb
+++ b/lib/api/import_github.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ImportGithub < Grape::API::Instance
+ class ImportGithub < ::API::Base
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
before do
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index ff687a57888..6d8f13c36e6 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -3,7 +3,7 @@
module API
# Internal access API
module Internal
- class Base < Grape::API::Instance
+ class Base < ::API::Base
before { authenticate_by_gitlab_shell_token! }
before do
@@ -99,6 +99,14 @@ module API
@project = @container = access_checker.container
end
end
+
+ def validate_actor_key(actor, key_id)
+ return 'Could not find a user without a key' unless key_id
+
+ return 'Could not find the given key' unless actor.key
+
+ 'Could not find a user for the given key' unless actor.user
+ end
end
namespace 'internal' do
@@ -163,28 +171,23 @@ module API
redis: redis_ping
}
end
+
post '/two_factor_recovery_codes' do
status 200
actor.update_last_used_at!
user = actor.user
- if params[:key_id]
- unless actor.key
- break { success: false, message: 'Could not find the given key' }
- end
-
- if actor.key.is_a?(DeployKey)
- break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
- end
+ error_message = validate_actor_key(actor, params[:key_id])
- unless user
- break { success: false, message: 'Could not find a user for the given key' }
- end
- elsif params[:user_id] && user.nil?
+ if params[:user_id] && user.nil?
break { success: false, message: 'Could not find the given user' }
+ elsif error_message
+ break { success: false, message: error_message }
end
+ break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' } if actor.key.is_a?(DeployKey)
+
unless user.two_factor_enabled?
break { success: false, message: 'Two-factor authentication is not enabled for this user' }
end
@@ -204,20 +207,14 @@ module API
actor.update_last_used_at!
user = actor.user
- if params[:key_id]
- unless actor.key
- break { success: false, message: 'Could not find the given key' }
- end
+ error_message = validate_actor_key(actor, params[:key_id])
- if actor.key.is_a?(DeployKey)
- break { success: false, message: 'Deploy keys cannot be used to create personal access tokens' }
- end
+ break { success: false, message: 'Deploy keys cannot be used to create personal access tokens' } if actor.key.is_a?(DeployKey)
- unless user
- break { success: false, message: 'Could not find a user for the given key' }
- end
- elsif params[:user_id] && user.nil?
+ if params[:user_id] && user.nil?
break { success: false, message: 'Could not find the given user' }
+ elsif error_message
+ break { success: false, message: error_message }
end
if params[:name].blank?
@@ -269,6 +266,53 @@ module API
present response, with: Entities::InternalPostReceive::Response
end
+
+ post '/two_factor_config' do
+ status 200
+
+ break { success: false } unless Feature.enabled?(:two_factor_for_cli)
+
+ actor.update_last_used_at!
+ user = actor.user
+
+ error_message = validate_actor_key(actor, params[:key_id])
+
+ if error_message
+ { success: false, message: error_message }
+ elsif actor.key.is_a?(DeployKey)
+ { success: true, two_factor_required: false }
+ else
+ {
+ success: true,
+ two_factor_required: user.two_factor_enabled?
+ }
+ end
+ end
+
+ post '/two_factor_otp_check' do
+ status 200
+
+ break { success: false } unless Feature.enabled?(:two_factor_for_cli)
+
+ actor.update_last_used_at!
+ user = actor.user
+
+ error_message = validate_actor_key(actor, params[:key_id])
+
+ break { success: false, message: error_message } if error_message
+
+ break { success: false, message: 'Deploy keys cannot be used for Two Factor' } if actor.key.is_a?(DeployKey)
+
+ break { success: false, message: 'Two-factor authentication is not enabled for this user' } unless user.two_factor_enabled?
+
+ otp_validation_result = ::Users::ValidateOtpService.new(user).execute(params.fetch(:otp_attempt))
+
+ if otp_validation_result[:status] == :success
+ { success: true }
+ else
+ { success: false, message: 'Invalid OTP' }
+ end
+ end
end
end
end
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index 6d5dfd086e7..8175b81f900 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -3,7 +3,7 @@
module API
# Kubernetes Internal API
module Internal
- class Kubernetes < Grape::API::Instance
+ class Kubernetes < ::API::Base
before do
check_feature_enabled
authenticate_gitlab_kas_request!
diff --git a/lib/api/internal/lfs.rb b/lib/api/internal/lfs.rb
new file mode 100644
index 00000000000..630f0ec77a8
--- /dev/null
+++ b/lib/api/internal/lfs.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module API
+ module Internal
+ class Lfs < ::API::Base
+ use Rack::Sendfile
+
+ before { authenticate_by_gitlab_shell_token! }
+
+ helpers do
+ def find_lfs_object(lfs_oid)
+ LfsObject.find_by_oid(lfs_oid)
+ end
+ end
+
+ namespace 'internal' do
+ namespace 'lfs' do
+ desc 'Get LFS URL for object ID' do
+ detail 'This feature was introduced in GitLab 13.5.'
+ end
+ params do
+ requires :oid, type: String, desc: 'The object ID to query'
+ requires :gl_repository, type: String, desc: "Project identifier (e.g. project-1)"
+ end
+ get "/" do
+ lfs_object = find_lfs_object(params[:oid])
+
+ not_found! unless lfs_object
+
+ _, project, repo_type = Gitlab::GlRepository.parse(params[:gl_repository])
+
+ not_found! unless repo_type.project? && project
+ not_found! unless lfs_object.project_allowed_access?(project)
+
+ file = lfs_object.file
+
+ not_found! unless file&.exists?
+
+ content_type 'application/octet-stream'
+
+ if file.file_storage?
+ sendfile file.path
+ else
+ workhorse_headers = Gitlab::Workhorse.send_url(file.url)
+ header workhorse_headers[0], workhorse_headers[1]
+ env['api.format'] = :binary
+ body ""
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/internal/pages.rb b/lib/api/internal/pages.rb
index 5f8d23f15fa..51136144c19 100644
--- a/lib/api/internal/pages.rb
+++ b/lib/api/internal/pages.rb
@@ -3,7 +3,7 @@
module API
# Pages Internal API
module Internal
- class Pages < Grape::API::Instance
+ class Pages < ::API::Base
before do
authenticate_gitlab_pages_request!
end
diff --git a/lib/api/issue_links.rb b/lib/api/issue_links.rb
index 6cc5b344f47..db4979c9052 100644
--- a/lib/api/issue_links.rb
+++ b/lib/api/issue_links.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class IssueLinks < Grape::API::Instance
+ class IssueLinks < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 0e5b0fae6e2..143f9e40736 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Issues < Grape::API::Instance
+ class Issues < ::API::Base
include PaginationParams
helpers Helpers::IssuesHelpers
helpers Helpers::RateLimiter
@@ -231,9 +231,6 @@ module API
authorize! :create_issue, user_project
- params.delete(:created_at) unless current_user.can?(:set_issue_created_at, user_project)
- params.delete(:iid) unless current_user.can?(:set_issue_iid, user_project)
-
issue_params = declared_params(include_missing: false)
issue_params[:system_note_timestamp] = params[:created_at]
@@ -279,8 +276,6 @@ module API
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
- # Setting updated_at is allowed only for admins and owners
- params.delete(:updated_at) unless current_user.can?(:set_issue_updated_at, user_project)
issue.system_note_timestamp = params[:updated_at]
update_params = declared_params(include_missing: false).merge(request: request, api: true)
diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb
index bc7bc956580..536b361b308 100644
--- a/lib/api/job_artifacts.rb
+++ b/lib/api/job_artifacts.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class JobArtifacts < Grape::API::Instance
+ class JobArtifacts < ::API::Base
before { authenticate_non_get! }
# EE::API::JobArtifacts would override the following helpers
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index ad46d948f3b..bdb23b4a9be 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Jobs < Grape::API::Instance
+ class Jobs < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/keys.rb b/lib/api/keys.rb
index c014641ca04..2e4568029b5 100644
--- a/lib/api/keys.rb
+++ b/lib/api/keys.rb
@@ -2,7 +2,7 @@
module API
# Keys API
- class Keys < Grape::API::Instance
+ class Keys < ::API::Base
before { authenticate! }
resource :keys do
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index edf4a8ca14e..0cc9f33bd07 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Labels < Grape::API::Instance
+ class Labels < ::API::Base
include PaginationParams
helpers ::API::Helpers::LabelHelpers
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index f7796b1e969..bfd152f70b1 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -1,24 +1,48 @@
# frozen_string_literal: true
module API
- class Lint < Grape::API::Instance
+ class Lint < ::API::Base
namespace :ci do
desc 'Validation of .gitlab-ci.yml content'
params do
requires :content, type: String, desc: 'Content of .gitlab-ci.yml'
+ optional :include_merged_yaml, type: Boolean, desc: 'Whether or not to include merged CI config yaml in the response'
end
post '/lint' do
- error = Gitlab::Ci::YamlProcessor.validation_message(params[:content],
- user: current_user)
+ result = Gitlab::Ci::YamlProcessor.new(params[:content], user: current_user).execute
+ error = result.errors.first
status 200
- if error.blank?
- { status: 'valid', errors: [] }
- else
- { status: 'invalid', errors: [error] }
+ response = if error.blank?
+ { status: 'valid', errors: [] }
+ else
+ { status: 'invalid', errors: [error] }
+ end
+
+ response.tap do |response|
+ response[:merged_yaml] = result.merged_yaml if params[:include_merged_yaml]
end
end
end
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'Validation of .gitlab-ci.yml content' do
+ detail 'This feature was introduced in GitLab 13.5.'
+ end
+ params do
+ optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
+ end
+ get ':id/ci/lint' do
+ authorize! :download_code, user_project
+
+ content = user_project.repository.gitlab_ci_yml_for(user_project.commit.id, user_project.ci_config_path_or_default)
+ result = Gitlab::Ci::Lint
+ .new(project: user_project, current_user: current_user)
+ .validate(content, dry_run: params[:dry_run])
+
+ present result, with: Entities::Ci::Lint::Result, current_user: current_user
+ end
+ end
end
end
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index a0822271cca..97549abd273 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Markdown < Grape::API::Instance
+ class Markdown < ::API::Base
params do
requires :text, type: String, desc: "The markdown text to render"
optional :gfm, type: Boolean, desc: "Render text using GitLab Flavored Markdown"
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index e6d9a9a7c20..a3e2fa84c32 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
module API
- class MavenPackages < Grape::API::Instance
+ class MavenPackages < ::API::Base
MAVEN_ENDPOINT_REQUIREMENTS = {
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
@@ -32,10 +32,10 @@ module API
end
def verify_package_file(package_file, uploaded_file)
- stored_sha1 = Digest::SHA256.hexdigest(package_file.file_sha1)
- expected_sha1 = uploaded_file.sha256
+ stored_sha256 = Digest::SHA256.hexdigest(package_file.file_sha1)
+ expected_sha256 = uploaded_file.sha256
- if stored_sha1 == expected_sha1
+ if stored_sha256 == expected_sha256
no_content!
else
conflict!
@@ -107,7 +107,7 @@ module API
when 'sha1'
package_file.file_sha1
else
- package_event('pull_package') if jar_file?(format)
+ track_package_event('pull_package', :maven) if jar_file?(format)
present_carrierwave_file_with_head_support!(package_file.file)
end
end
@@ -145,7 +145,7 @@ module API
when 'sha1'
package_file.file_sha1
else
- package_event('pull_package') if jar_file?(format)
+ track_package_event('pull_package', :maven) if jar_file?(format)
present_carrierwave_file_with_head_support!(package_file.file)
end
@@ -181,7 +181,7 @@ module API
when 'sha1'
package_file.file_sha1
else
- package_event('pull_package') if jar_file?(format)
+ track_package_event('pull_package', :maven) if jar_file?(format)
present_carrierwave_file_with_head_support!(package_file.file)
end
@@ -231,9 +231,9 @@ module API
verify_package_file(package_file, params[:file])
when 'md5'
- nil
+ ''
else
- package_event('push_package') if jar_file?(format)
+ track_package_event('push_package', :maven) if jar_file?(format)
file_params = {
file: params[:file],
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 4edf94c6350..c28b3b1cc7c 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Members < Grape::API::Instance
+ class Members < ::API::Base
include PaginationParams
before { authenticate! }
@@ -88,8 +88,8 @@ module API
success Entities::Member
end
params do
- requires :user_id, type: Integer, desc: 'The user ID of the new member'
requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)'
+ requires :user_id, types: [Integer, String], desc: 'The user ID of the new member or multiple IDs separated by commas.'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -97,20 +97,26 @@ module API
source = find_source(source_type, params[:id])
authorize_admin_source!(source_type, source)
- member = source.members.find_by(user_id: params[:user_id])
- conflict!('Member already exists') if member
+ if params[:user_id].to_s.include?(',')
+ create_service_params = params.except(:user_id).merge({ user_ids: params[:user_id] })
- user = User.find_by_id(params[:user_id])
- not_found!('User') unless user
+ ::Members::CreateService.new(current_user, create_service_params).execute(source)
+ elsif params[:user_id].present?
+ member = source.members.find_by(user_id: params[:user_id])
+ conflict!('Member already exists') if member
- member = create_member(current_user, user, source, params)
+ user = User.find_by_id(params[:user_id])
+ not_found!('User') unless user
- if !member
- not_allowed! # This currently can only be reached in EE
- elsif member.valid? && member.persisted?
- present_members(member)
- else
- render_validation_error!(member)
+ member = create_member(current_user, user, source, params)
+
+ if !member
+ not_allowed! # This currently can only be reached in EE
+ elsif member.valid? && member.persisted?
+ present_members(member)
+ else
+ render_validation_error!(member)
+ end
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/merge_request_approvals.rb b/lib/api/merge_request_approvals.rb
index 035ed9f0e04..14d6e3995ea 100644
--- a/lib/api/merge_request_approvals.rb
+++ b/lib/api/merge_request_approvals.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class MergeRequestApprovals < ::Grape::API::Instance
+ class MergeRequestApprovals < ::API::Base
before { authenticate_non_get! }
helpers do
diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb
index 3e43fe8b257..22023888bbd 100644
--- a/lib/api/merge_request_diffs.rb
+++ b/lib/api/merge_request_diffs.rb
@@ -2,7 +2,7 @@
module API
# MergeRequestDiff API
- class MergeRequestDiffs < Grape::API::Instance
+ class MergeRequestDiffs < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 4bd72b267a9..b24dd870c8b 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class MergeRequests < Grape::API::Instance
+ class MergeRequests < ::API::Base
include PaginationParams
CONTEXT_COMMITS_POST_LIMIT = 20
diff --git a/lib/api/metrics/dashboard/annotations.rb b/lib/api/metrics/dashboard/annotations.rb
index e07762ac6d3..b6bc0af2202 100644
--- a/lib/api/metrics/dashboard/annotations.rb
+++ b/lib/api/metrics/dashboard/annotations.rb
@@ -3,7 +3,7 @@
module API
module Metrics
module Dashboard
- class Annotations < Grape::API::Instance
+ class Annotations < ::API::Base
desc 'Create a new monitoring dashboard annotation' do
success Entities::Metrics::Dashboard::Annotation
end
diff --git a/lib/api/metrics/user_starred_dashboards.rb b/lib/api/metrics/user_starred_dashboards.rb
index 263d2394276..cb6e7099247 100644
--- a/lib/api/metrics/user_starred_dashboards.rb
+++ b/lib/api/metrics/user_starred_dashboards.rb
@@ -2,7 +2,7 @@
module API
module Metrics
- class UserStarredDashboards < Grape::API::Instance
+ class UserStarredDashboards < ::API::Base
resource :projects do
desc 'Marks selected metrics dashboard as starred' do
success Entities::Metrics::UserStarredDashboard
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index e1f279df045..f98a1f6dd1d 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Namespaces < Grape::API::Instance
+ class Namespaces < ::API::Base
include PaginationParams
before { authenticate! }
@@ -32,7 +32,9 @@ module API
get do
namespaces = current_user.admin ? Namespace.all : current_user.namespaces
- namespaces = namespaces.include_gitlab_subscription if Gitlab.ee?
+ namespaces = namespaces.include_route
+
+ namespaces = namespaces.include_gitlab_subscription_with_hosted_plan if Gitlab.ee?
namespaces = namespaces.search(params[:search]) if params[:search].present?
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index e4989243f3d..0db537ca616 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Notes < Grape::API::Instance
+ class Notes < ::API::Base
include PaginationParams
helpers ::API::Helpers::NotesHelpers
diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb
index f8b621c1c38..bad3f5ead7a 100644
--- a/lib/api/notification_settings.rb
+++ b/lib/api/notification_settings.rb
@@ -2,7 +2,7 @@
module API
# notification_settings API
- class NotificationSettings < Grape::API::Instance
+ class NotificationSettings < ::API::Base
before { authenticate! }
helpers ::API::Helpers::MembersHelpers
diff --git a/lib/api/npm_packages.rb b/lib/api/npm_packages.rb
index fca405b76b7..1443b28c1ee 100644
--- a/lib/api/npm_packages.rb
+++ b/lib/api/npm_packages.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
module API
- class NpmPackages < Grape::API::Instance
+ class NpmPackages < ::API::Base
helpers ::API::Helpers::PackagesHelpers
helpers ::API::Helpers::Packages::DependencyProxyHelpers
@@ -141,7 +141,7 @@ module API
package_file = ::Packages::PackageFileFinder
.new(package, params[:file_name]).execute!
- package_event('pull_package')
+ track_package_event('pull_package', package)
present_carrierwave_file!(package_file.file)
end
@@ -157,7 +157,7 @@ module API
put ':id/packages/npm/:package_name', requirements: NPM_ENDPOINT_REQUIREMENTS do
authorize_create_package!(user_project)
- package_event('push_package')
+ track_package_event('push_package', :npm)
created_package = ::Packages::Npm::CreatePackageService
.new(user_project, current_user, params.merge(build: current_authenticated_job)).execute
diff --git a/lib/api/nuget_packages.rb b/lib/api/nuget_packages.rb
index f84a3acbe6d..0f2c956a9df 100644
--- a/lib/api/nuget_packages.rb
+++ b/lib/api/nuget_packages.rb
@@ -6,7 +6,7 @@
# called by the NuGet package manager client when users run commands
# like `nuget install` or `nuget push`.
module API
- class NugetPackages < Grape::API::Instance
+ class NugetPackages < ::API::Base
helpers ::API::Helpers::PackagesManagerClientsHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
@@ -42,7 +42,7 @@ module API
def package_finder(finder_params = {})
::Packages::Nuget::PackageFinder.new(
authorized_user_project,
- finder_params.merge(package_name: params[:package_name])
+ **finder_params.merge(package_name: params[:package_name])
)
end
end
@@ -73,7 +73,7 @@ module API
get 'index', format: :json do
authorize_read_package!(authorized_user_project)
- track_event('nuget_service_index')
+ track_package_event('cli_metadata', :nuget)
present ::Packages::Nuget::ServiceIndexPresenter.new(authorized_user_project),
with: ::API::Entities::Nuget::ServiceIndex
@@ -105,7 +105,7 @@ module API
package_file = ::Packages::CreatePackageFileService.new(package, file_params)
.execute
- package_event('push_package')
+ track_package_event('push_package', :nuget)
::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
@@ -198,7 +198,7 @@ module API
not_found!('Package') unless package_file
- package_event('pull_package')
+ track_package_event('pull_package', :nuget)
# 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)
@@ -233,7 +233,7 @@ module API
.new(authorized_user_project, params[:q], search_options)
.execute
- package_event('search_package')
+ track_package_event('search_package', :nuget)
present ::Packages::Nuget::SearchResultsPresenter.new(search),
with: ::API::Entities::Nuget::SearchResults
diff --git a/lib/api/package_files.rb b/lib/api/package_files.rb
index 17b92df629c..c1fc9a6e4d8 100644
--- a/lib/api/package_files.rb
+++ b/lib/api/package_files.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class PackageFiles < Grape::API::Instance
+ class PackageFiles < ::API::Base
include PaginationParams
before do
diff --git a/lib/api/pages.rb b/lib/api/pages.rb
index 79a6b527581..813307c498f 100644
--- a/lib/api/pages.rb
+++ b/lib/api/pages.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Pages < Grape::API::Instance
+ class Pages < ::API::Base
before do
require_pages_config_enabled!
authenticated_with_can_read_all_resources!
diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb
index 7d27b575efa..00c51298c45 100644
--- a/lib/api/pages_domains.rb
+++ b/lib/api/pages_domains.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class PagesDomains < Grape::API::Instance
+ class PagesDomains < ::API::Base
include PaginationParams
PAGES_DOMAINS_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(domain: API::NO_SLASH_URL_PART_REGEX)
diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb
index 0e5605984e6..46ccb4ba1a0 100644
--- a/lib/api/project_clusters.rb
+++ b/lib/api/project_clusters.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectClusters < Grape::API::Instance
+ class ProjectClusters < ::API::Base
include PaginationParams
before { authenticate! }
@@ -45,6 +45,7 @@ module API
optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true'
optional :domain, type: String, desc: 'Cluster base domain'
optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster'
+ optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true'
requires :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
@@ -78,6 +79,7 @@ module API
optional :name, type: String, desc: 'Cluster name'
optional :domain, type: String, desc: 'Cluster base domain'
optional :environment_scope, type: String, desc: 'The associated environment to the cluster'
+ optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb
index 8f2a62bc5a4..d565531d372 100644
--- a/lib/api/project_container_repositories.rb
+++ b/lib/api/project_container_repositories.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
module API
- class ProjectContainerRepositories < Grape::API::Instance
+ class ProjectContainerRepositories < ::API::Base
include PaginationParams
+ helpers ::API::Helpers::PackagesHelpers
REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
tag_name: API::NO_SLASH_URL_PART_REGEX)
- before { error!('404 Not Found', 404) unless Feature.enabled?(:container_registry_api, user_project, default_enabled: true) }
before { authorize_read_container_images! }
params do
@@ -28,7 +28,7 @@ module API
user: current_user, subject: user_project
).execute
- track_event( 'list_repositories')
+ track_package_event('list_repositories', :container)
present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count]
end
@@ -43,7 +43,7 @@ module API
authorize_admin_container_image!
DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) # rubocop:disable CodeReuse/Worker
- track_event('delete_repository')
+ track_package_event('delete_repository', :container)
status :accepted
end
@@ -60,7 +60,7 @@ module API
authorize_read_container_image!
tags = Kaminari.paginate_array(repository.tags)
- track_event('list_tags')
+ track_package_event('list_tags', :container)
present paginate(tags), with: Entities::ContainerRegistry::Tag
end
@@ -89,7 +89,7 @@ module API
declared_params.except(:repository_id).merge(container_expiration_policy: false))
# rubocop:enable CodeReuse/Worker
- track_event('delete_tag_bulk')
+ track_package_event('delete_tag_bulk', :container)
status :accepted
end
@@ -125,7 +125,7 @@ module API
.execute(repository)
if result[:status] == :success
- track_event('delete_tag')
+ track_package_event('delete_tag', :container)
status :ok
else
diff --git a/lib/api/project_events.rb b/lib/api/project_events.rb
index 726e693826e..3765473bc0e 100644
--- a/lib/api/project_events.rb
+++ b/lib/api/project_events.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectEvents < Grape::API::Instance
+ class ProjectEvents < ::API::Base
include PaginationParams
include APIGuard
helpers ::API::Helpers::EventsHelpers
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index 377d61689b3..184f89200ab 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectExport < Grape::API::Instance
+ class ProjectExport < ::API::Base
helpers Helpers::RateLimiter
before do
@@ -55,7 +55,7 @@ module API
export_strategy = if after_export_params[:url].present?
params = after_export_params.slice(:url, :http_method).symbolize_keys
- Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy.new(params)
+ Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy.new(**params)
end
if export_strategy&.invalid?
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index e68a3b106b1..bc2d8c816a8 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectHooks < Grape::API::Instance
+ class ProjectHooks < ::API::Base
include PaginationParams
before { authenticate! }
@@ -104,7 +104,9 @@ module API
delete ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params.delete(:hook_id))
- destroy_conditionally!(hook)
+ destroy_conditionally!(hook) do
+ WebHooks::DestroyService.new(current_user).execute(hook)
+ end
end
end
end
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index 9f43c3c7993..5c4e1d73ee1 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
module API
- class ProjectImport < Grape::API::Instance
+ class ProjectImport < ::API::Base
include PaginationParams
- MAXIMUM_FILE_SIZE = 50.megabytes
-
helpers Helpers::ProjectsHelpers
helpers Helpers::FileUploadHelpers
helpers Helpers::RateLimiter
diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb
index 2f8dd1085dc..a81118f44bd 100644
--- a/lib/api/project_milestones.rb
+++ b/lib/api/project_milestones.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectMilestones < Grape::API::Instance
+ class ProjectMilestones < ::API::Base
include PaginationParams
include MilestoneResponses
diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb
index 359514f1f78..b8d97b1243a 100644
--- a/lib/api/project_packages.rb
+++ b/lib/api/project_packages.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectPackages < Grape::API::Instance
+ class ProjectPackages < ::API::Base
include PaginationParams
before do
diff --git a/lib/api/project_repository_storage_moves.rb b/lib/api/project_repository_storage_moves.rb
index c318907542b..38eb74663d3 100644
--- a/lib/api/project_repository_storage_moves.rb
+++ b/lib/api/project_repository_storage_moves.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectRepositoryStorageMoves < Grape::API::Instance
+ class ProjectRepositoryStorageMoves < ::API::Base
include PaginationParams
before { authenticated_as_admin! }
@@ -69,7 +69,7 @@ module API
success Entities::ProjectRepositoryStorageMove
end
params do
- requires :destination_storage_name, type: String, desc: 'The destination storage shard'
+ optional :destination_storage_name, type: String, desc: 'The destination storage shard'
end
post ':id/repository_storage_moves' do
storage_move = user_project.repository_storage_moves.build(
diff --git a/lib/api/project_snapshots.rb b/lib/api/project_snapshots.rb
index 360000861fc..e19afb6e8e4 100644
--- a/lib/api/project_snapshots.rb
+++ b/lib/api/project_snapshots.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectSnapshots < Grape::API::Instance
+ class ProjectSnapshots < ::API::Base
helpers ::API::Helpers::ProjectSnapshotsHelpers
before { authorize_read_git_snapshot! }
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index f6e87fece89..b4de260fe49 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -1,10 +1,9 @@
# frozen_string_literal: true
module API
- class ProjectSnippets < Grape::API::Instance
+ class ProjectSnippets < ::API::Base
include PaginationParams
- before { authenticate! }
before { check_snippets_enabled }
params do
@@ -37,6 +36,8 @@ module API
use :pagination
end
get ":id/snippets" do
+ authenticate!
+
present paginate(snippets_for_current_user), with: Entities::ProjectSnippet, current_user: current_user
end
@@ -48,6 +49,9 @@ module API
end
get ":id/snippets/:snippet_id" do
snippet = snippets_for_current_user.find(params[:snippet_id])
+
+ not_found!('Snippet') unless snippet
+
present snippet, with: Entities::ProjectSnippet, current_user: current_user
end
@@ -63,6 +67,8 @@ module API
use :create_file_params
end
post ":id/snippets" do
+ authenticate!
+
authorize! :create_snippet, user_project
snippet_params = process_create_params(declared_params(include_missing: false))
@@ -97,6 +103,8 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
put ":id/snippets/:snippet_id" do
+ authenticate!
+
snippet = snippets_for_current_user.find_by(id: params.delete(:snippet_id))
not_found!('Snippet') unless snippet
@@ -125,6 +133,8 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
delete ":id/snippets/:snippet_id" do
+ authenticate!
+
snippet = snippets_for_current_user.find_by(id: params[:snippet_id])
not_found!('Snippet') unless snippet
diff --git a/lib/api/project_statistics.rb b/lib/api/project_statistics.rb
index 2196801096f..1ead969fc81 100644
--- a/lib/api/project_statistics.rb
+++ b/lib/api/project_statistics.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectStatistics < Grape::API::Instance
+ class ProjectStatistics < ::API::Base
before do
authenticate!
authorize! :daily_statistics, user_project
diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb
index 48c3dbed3b0..7d851de0237 100644
--- a/lib/api/project_templates.rb
+++ b/lib/api/project_templates.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProjectTemplates < Grape::API::Instance
+ class ProjectTemplates < ::API::Base
include PaginationParams
TEMPLATE_TYPES = %w[dockerfiles gitignores gitlab_ci_ymls licenses metrics_dashboard_ymls issues merge_requests].freeze
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index abbdb11a3f7..ecee76ae60c 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -3,7 +3,7 @@
require_dependency 'declarative_policy'
module API
- class Projects < Grape::API::Instance
+ class Projects < ::API::Base
include PaginationParams
include Helpers::CustomAttributes
@@ -353,7 +353,7 @@ module API
optional :path, type: String, desc: 'The path of the repository'
use :optional_project_params
- use :optional_update_params_ee
+ use :optional_update_params
at_least_one_of(*Helpers::ProjectsHelpers.update_params_at_least_one_of)
end
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index b0a7f898eec..a448682d8bd 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProtectedBranches < Grape::API::Instance
+ class ProtectedBranches < ::API::Base
include PaginationParams
BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
diff --git a/lib/api/protected_tags.rb b/lib/api/protected_tags.rb
index aaa31cb7cc6..dd3e407ffc9 100644
--- a/lib/api/protected_tags.rb
+++ b/lib/api/protected_tags.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ProtectedTags < Grape::API::Instance
+ class ProtectedTags < ::API::Base
include PaginationParams
TAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index c07db68f8a8..5622bc6e42d 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -6,7 +6,7 @@
# called by the PyPI package manager client when users run commands
# like `pip install` or `twine upload`.
module API
- class PypiPackages < Grape::API::Instance
+ class PypiPackages < ::API::Base
helpers ::API::Helpers::PackagesManagerClientsHelpers
helpers ::API::Helpers::RelatedResourcesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
@@ -33,7 +33,7 @@ module API
def find_package_versions
packages = packages_finder
- .with_name(params[:package_name])
+ .with_normalized_pypi_name(params[:package_name])
not_found!('Package') if packages.empty?
@@ -72,7 +72,7 @@ module API
package = packages_finder(project).by_file_name_and_sha256(filename, params[:sha256])
package_file = ::Packages::PackageFileFinder.new(package, filename, with_file_name_like: false).execute
- package_event('pull_package')
+ track_package_event('pull_package', :pypi)
present_carrierwave_file!(package_file.file, supports_direct_download: true)
end
@@ -91,7 +91,7 @@ module API
get 'simple/*package_name', format: :txt do
authorize_read_package!(authorized_user_project)
- package_event('list_package')
+ track_package_event('list_package', :pypi)
packages = find_package_versions
presenter = ::Packages::Pypi::PackagePresenter.new(packages, authorized_user_project)
@@ -122,7 +122,7 @@ module API
authorize_upload!(authorized_user_project)
bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:pypi_max_file_size, params[:content].size)
- package_event('push_package')
+ track_package_event('push_package', :pypi)
::Packages::Pypi::CreatePackageService
.new(authorized_user_project, current_user, declared_params)
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index 9624b8924e5..23de9f9fc9f 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -2,7 +2,7 @@
module API
module Release
- class Links < Grape::API::Instance
+ class Links < ::API::Base
include PaginationParams
RELEASE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index 3c38721129f..3bd6ea77403 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Releases < Grape::API::Instance
+ class Releases < ::API::Base
include PaginationParams
RELEASE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
@@ -19,9 +19,13 @@ module API
end
params do
use :pagination
+ optional :order_by, type: String, values: %w[released_at created_at], default: 'released_at',
+ desc: 'Return releases ordered by `released_at` or `created_at`.'
+ optional :sort, type: String, values: %w[asc desc], default: 'desc',
+ desc: 'Return releases sorted in `asc` or `desc` order.'
end
get ':id/releases' do
- releases = ::ReleasesFinder.new(user_project, current_user).execute
+ releases = ::ReleasesFinder.new(user_project, current_user, declared_params.slice(:order_by, :sort)).execute
present paginate(releases), with: Entities::Release, current_user: current_user
end
@@ -152,7 +156,7 @@ module API
end
def authorize_create_evidence!
- # This is a separate method so that EE can extend its behaviour
+ # extended in EE
end
def release
@@ -160,15 +164,15 @@ module API
end
def log_release_created_audit_event(release)
- # This is a separate method so that EE can extend its behaviour
+ # extended in EE
end
def log_release_updated_audit_event
- # This is a separate method so that EE can extend its behaviour
+ # extended in EE
end
def log_release_milestones_updated_audit_event
- # This is a separate method so that EE can extend its behaviour
+ # extended in EE
end
end
end
diff --git a/lib/api/remote_mirrors.rb b/lib/api/remote_mirrors.rb
index d1def05808b..f63ea04a529 100644
--- a/lib/api/remote_mirrors.rb
+++ b/lib/api/remote_mirrors.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class RemoteMirrors < Grape::API::Instance
+ class RemoteMirrors < ::API::Base
include PaginationParams
before do
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 81702f8f02a..38ac1f22a48 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -3,7 +3,7 @@
require 'mime/types'
module API
- class Repositories < Grape::API::Instance
+ class Repositories < ::API::Base
include PaginationParams
helpers ::API::Helpers::HeadersHelpers
diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb
index a8d3419528c..d3a219f0810 100644
--- a/lib/api/resource_label_events.rb
+++ b/lib/api/resource_label_events.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ResourceLabelEvents < Grape::API::Instance
+ class ResourceLabelEvents < ::API::Base
include PaginationParams
helpers ::API::Helpers::NotesHelpers
diff --git a/lib/api/resource_milestone_events.rb b/lib/api/resource_milestone_events.rb
index a8f221f8740..21411f68dd5 100644
--- a/lib/api/resource_milestone_events.rb
+++ b/lib/api/resource_milestone_events.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ResourceMilestoneEvents < Grape::API::Instance
+ class ResourceMilestoneEvents < ::API::Base
include PaginationParams
helpers ::API::Helpers::NotesHelpers
diff --git a/lib/api/resource_state_events.rb b/lib/api/resource_state_events.rb
index 1c1a90c09a3..9bfda39be90 100644
--- a/lib/api/resource_state_events.rb
+++ b/lib/api/resource_state_events.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class ResourceStateEvents < Grape::API::Instance
+ class ResourceStateEvents < ::API::Base
include PaginationParams
helpers ::API::Helpers::NotesHelpers
diff --git a/lib/api/search.rb b/lib/api/search.rb
index b9c6a823f4f..85f0a8e2e60 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Search < Grape::API::Instance
+ class Search < ::API::Base
include PaginationParams
before { authenticate! }
@@ -33,6 +33,7 @@ module API
scope: params[:scope],
search: params[:search],
state: params[:state],
+ confidential: params[:confidential],
snippets: snippets?,
page: params[:page],
per_page: params[:per_page]
@@ -62,12 +63,6 @@ module API
# Defining this method here as a noop allows us to easily extend it in
# EE, without having to modify this file directly.
end
-
- def check_users_search_allowed!
- if params[:scope].to_sym == :users && Feature.disabled?(:users_search, default_enabled: true)
- render_api_error!({ error: _("Scope not supported with disabled 'users_search' feature!") }, 400)
- end
- end
end
resource :search do
@@ -81,11 +76,11 @@ module API
desc: 'The scope of the search',
values: Helpers::SearchHelpers.global_search_scopes
optional :state, type: String, desc: 'Filter results by state', values: Helpers::SearchHelpers.search_states
+ optional :confidential, type: Boolean, desc: 'Filter results by confidentiality'
use :pagination
end
get do
verify_search_scope!(resource: nil)
- check_users_search_allowed!
present search, with: entity
end
@@ -103,11 +98,11 @@ module API
desc: 'The scope of the search',
values: Helpers::SearchHelpers.group_search_scopes
optional :state, type: String, desc: 'Filter results by state', values: Helpers::SearchHelpers.search_states
+ optional :confidential, type: Boolean, desc: 'Filter results by confidentiality'
use :pagination
end
get ':id/(-/)search' do
verify_search_scope!(resource: user_group)
- check_users_search_allowed!
present search(group_id: user_group.id), with: entity
end
@@ -126,11 +121,10 @@ module API
values: Helpers::SearchHelpers.project_search_scopes
optional :ref, type: String, desc: 'The name of a repository branch or tag. If not given, the default branch is used'
optional :state, type: String, desc: 'Filter results by state', values: Helpers::SearchHelpers.search_states
+ optional :confidential, type: Boolean, desc: 'Filter results by confidentiality'
use :pagination
end
get ':id/(-/)search' do
- check_users_search_allowed!
-
present search({ project_id: user_project.id, repository_ref: params[:ref] }), with: entity
end
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 9ee1822339c..5f3d14010a8 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
module API
- class Services < Grape::API::Instance
+ class Services < ::API::Base
services = Helpers::ServicesHelpers.services
service_classes = Helpers::ServicesHelpers.service_classes
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 6e5534d0c9a..dc917d9c529 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Settings < Grape::API::Instance
+ class Settings < ::API::Base
before { authenticated_as_admin! }
helpers Helpers::SettingsHelpers
@@ -29,7 +29,8 @@ module API
success Entities::ApplicationSetting
end
params do
- optional :admin_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.'
+ optional :admin_notification_email, type: String, desc: 'Deprecated: Use :abuse_notification_email instead. Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.'
+ optional :abuse_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.'
optional :after_sign_up_text, type: String, desc: 'Text shown after sign up'
optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out'
optional :akismet_enabled, type: Boolean, desc: 'Helps prevent bots from creating issues'
@@ -73,6 +74,7 @@ module API
optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled'
optional :help_page_hide_commercial_content, type: Boolean, desc: 'Hide marketing-related entries from help'
optional :help_page_support_url, type: String, desc: 'Alternate support URL for help page and help dropdown'
+ optional :help_page_documentation_base_url, type: String, desc: 'Alternate documentation pages URL'
optional :help_page_text, type: String, desc: 'Custom text displayed on the help page'
optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page'
optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)'
@@ -194,6 +196,11 @@ module API
attrs[:allow_local_requests_from_web_hooks_and_services] = attrs.delete(:allow_local_requests_from_hooks_and_services)
end
+ # support legacy names, can be removed in v5
+ if attrs.has_key?(:admin_notification_email)
+ attrs[:abuse_notification_email] = attrs.delete(:admin_notification_email)
+ end
+
# since 13.0 it's not possible to disable hashed storage - support can be removed in 14.0
attrs.delete(:hashed_storage_enabled) if attrs.has_key?(:hashed_storage_enabled)
diff --git a/lib/api/sidekiq_metrics.rb b/lib/api/sidekiq_metrics.rb
index 77f2b1e871e..b025dbfab37 100644
--- a/lib/api/sidekiq_metrics.rb
+++ b/lib/api/sidekiq_metrics.rb
@@ -3,7 +3,7 @@
require 'sidekiq/api'
module API
- class SidekiqMetrics < Grape::API::Instance
+ class SidekiqMetrics < ::API::Base
before { authenticated_as_admin! }
helpers do
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index c6ef35875fc..2e67b9649bc 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -2,11 +2,9 @@
module API
# Snippets API
- class Snippets < Grape::API::Instance
+ class Snippets < ::API::Base
include PaginationParams
- before { authenticate! }
-
resource :snippets do
helpers Helpers::SnippetsHelpers
helpers do
@@ -23,7 +21,7 @@ module API
end
end
- desc 'Get a snippets list for authenticated user' do
+ desc 'Get a snippets list for an authenticated user' do
detail 'This feature was introduced in GitLab 8.15.'
success Entities::Snippet
end
@@ -31,6 +29,8 @@ module API
use :pagination
end
get do
+ authenticate!
+
present paginate(snippets_for_current_user), with: Entities::Snippet, current_user: current_user
end
@@ -42,6 +42,8 @@ module API
use :pagination
end
get 'public' do
+ authenticate!
+
present paginate(public_snippets), with: Entities::PersonalSnippet, current_user: current_user
end
@@ -74,6 +76,8 @@ module API
use :create_file_params
end
post do
+ authenticate!
+
authorize! :create_snippet
attrs = process_create_params(declared_params(include_missing: false))
@@ -109,6 +113,8 @@ module API
use :minimum_update_params
end
put ':id' do
+ authenticate!
+
snippet = snippets_for_current_user.find_by_id(params.delete(:id))
break not_found!('Snippet') unless snippet
@@ -139,6 +145,8 @@ module API
requires :id, type: Integer, desc: 'The ID of a snippet'
end
delete ':id' do
+ authenticate!
+
snippet = snippets_for_current_user.find_by_id(params.delete(:id))
break not_found!('Snippet') unless snippet
diff --git a/lib/api/statistics.rb b/lib/api/statistics.rb
index 3869fd3ac76..fa7176491ba 100644
--- a/lib/api/statistics.rb
+++ b/lib/api/statistics.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Statistics < Grape::API::Instance
+ class Statistics < ::API::Base
before { authenticated_as_admin! }
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
diff --git a/lib/api/submodules.rb b/lib/api/submodules.rb
index 34d21d3d7d8..e2ceb49c119 100644
--- a/lib/api/submodules.rb
+++ b/lib/api/submodules.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Submodules < Grape::API::Instance
+ class Submodules < ::API::Base
before { authenticate! }
helpers do
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
index 533663fb087..35a28da4736 100644
--- a/lib/api/subscriptions.rb
+++ b/lib/api/subscriptions.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Subscriptions < Grape::API::Instance
+ class Subscriptions < ::API::Base
helpers ::API::Helpers::LabelHelpers
before { authenticate! }
diff --git a/lib/api/suggestions.rb b/lib/api/suggestions.rb
index 38e96c080f2..f23d279c3f4 100644
--- a/lib/api/suggestions.rb
+++ b/lib/api/suggestions.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Suggestions < Grape::API::Instance
+ class Suggestions < ::API::Base
before { authenticate! }
resource :suggestions do
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index d8e0a425625..2820d305d0f 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class SystemHooks < Grape::API::Instance
+ class SystemHooks < ::API::Base
include PaginationParams
before do
@@ -70,7 +70,9 @@ module API
hook = SystemHook.find_by(id: params[:id])
not_found!('System hook') unless hook
- destroy_conditionally!(hook)
+ destroy_conditionally!(hook) do
+ WebHooks::DestroyService.new(current_user).execute(hook)
+ end
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index c1fbd3ca7c6..b969394ec47 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Tags < Grape::API::Instance
+ class Tags < ::API::Base
include PaginationParams
TAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(tag_name: API::NO_SLASH_URL_PART_REGEX)
diff --git a/lib/api/templates.rb b/lib/api/templates.rb
index 80a97aae429..0b427bbf5b9 100644
--- a/lib/api/templates.rb
+++ b/lib/api/templates.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Templates < Grape::API::Instance
+ class Templates < ::API::Base
include PaginationParams
GLOBAL_TEMPLATE_TYPES = {
diff --git a/lib/api/terraform/state.rb b/lib/api/terraform/state.rb
index 7063a3d08b5..4168cce21ef 100644
--- a/lib/api/terraform/state.rb
+++ b/lib/api/terraform/state.rb
@@ -4,7 +4,7 @@ require_dependency 'api/validations/validators/limit'
module API
module Terraform
- class State < Grape::API::Instance
+ class State < ::API::Base
include ::Gitlab::Utils::StrongMemoize
default_format :json
diff --git a/lib/api/terraform/state_version.rb b/lib/api/terraform/state_version.rb
new file mode 100644
index 00000000000..b4a0efd7a2b
--- /dev/null
+++ b/lib/api/terraform/state_version.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module API
+ module Terraform
+ class StateVersion < ::API::Base
+ default_format :json
+
+ before do
+ authenticate!
+ authorize! :read_terraform_state, user_project
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ namespace ':id/terraform/state/:name/versions/:serial' do
+ params do
+ requires :name, type: String, desc: 'The name of a Terraform state'
+ requires :serial, type: Integer, desc: 'The version number of the state'
+ end
+
+ helpers do
+ def remote_state_handler
+ ::Terraform::RemoteStateHandler.new(user_project, current_user, name: params[:name])
+ end
+
+ def find_version(serial)
+ remote_state_handler.find_with_lock do |state|
+ version = state.versions.find_by_version(serial)
+
+ if version.present?
+ yield version
+ else
+ not_found!
+ end
+ end
+ end
+ end
+
+ desc 'Get a terraform state version'
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ get do
+ find_version(params[:serial]) do |version|
+ env['api.format'] = :binary # Bypass json serialization
+ body version.file.read
+ status :ok
+ end
+ end
+
+ desc 'Delete a terraform state version'
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
+ delete do
+ authorize! :admin_terraform_state, user_project
+
+ find_version(params[:serial]) do |version|
+ version.destroy!
+
+ body false
+ status :no_content
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 5eae92a251e..ce07d13cc9a 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Todos < Grape::API::Instance
+ class Todos < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index f398bbf3e32..960d004a04c 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Triggers < Grape::API::Instance
+ class Triggers < ::API::Base
include PaginationParams
HTTP_GITLAB_EVENT_HEADER = "HTTP_#{WebHookService::GITLAB_EVENT_HEADER}".underscore.upcase
diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb
new file mode 100644
index 00000000000..907422118f1
--- /dev/null
+++ b/lib/api/unleash.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+module API
+ class Unleash < ::API::Base
+ include PaginationParams
+
+ namespace :feature_flags do
+ resource :unleash, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ params do
+ requires :project_id, type: String, desc: 'The ID of a project'
+ optional :instance_id, type: String, desc: 'The Instance ID of Unleash Client'
+ optional :app_name, type: String, desc: 'The Application Name of Unleash Client'
+ end
+ route_param :project_id do
+ before do
+ authorize_by_unleash_instance_id!
+ end
+
+ get do
+ # not supported yet
+ status :ok
+ end
+
+ desc 'Get a list of features (deprecated, v2 client support)'
+ get 'features' do
+ present :version, 1
+ present :features, feature_flags, with: ::API::Entities::UnleashFeature
+ end
+
+ desc 'Get a list of features'
+ get 'client/features' do
+ present :version, 1
+ present :features, feature_flags, with: ::API::Entities::UnleashFeature
+ end
+
+ post 'client/register' do
+ # not supported yet
+ status :ok
+ end
+
+ post 'client/metrics' do
+ # not supported yet
+ status :ok
+ end
+ end
+ end
+ end
+
+ helpers do
+ def project
+ @project ||= find_project(params[:project_id])
+ end
+
+ def unleash_instance_id
+ env['HTTP_UNLEASH_INSTANCEID'] || params[:instance_id]
+ end
+
+ def unleash_app_name
+ env['HTTP_UNLEASH_APPNAME'] || params[:app_name]
+ end
+
+ def authorize_by_unleash_instance_id!
+ unauthorized! unless Operations::FeatureFlagsClient
+ .find_for_project_and_token(project, unleash_instance_id)
+ end
+
+ def feature_flags
+ return [] unless unleash_app_name.present?
+
+ legacy_flags = Operations::FeatureFlagScope.for_unleash_client(project, unleash_app_name)
+ new_version_flags = Operations::FeatureFlag.for_unleash_client(project, unleash_app_name)
+
+ legacy_flags + new_version_flags
+ end
+ end
+ end
+end
diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb
index a1512197ee1..fa5bfc1cbe9 100644
--- a/lib/api/usage_data.rb
+++ b/lib/api/usage_data.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
module API
- class UsageData < Grape::API::Instance
+ class UsageData < ::API::Base
before { authenticate! }
namespace 'usage_data' do
before do
- not_found! unless Feature.enabled?(:usage_data_api)
+ not_found! unless Feature.enabled?(:usage_data_api, default_enabled: true)
forbidden!('Invalid CSRF token is provided') unless verified_request?
end
diff --git a/lib/api/user_counts.rb b/lib/api/user_counts.rb
index 90127ecbc73..6d9db53fec8 100644
--- a/lib/api/user_counts.rb
+++ b/lib/api/user_counts.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class UserCounts < Grape::API::Instance
+ class UserCounts < ::API::Base
resource :user_counts do
desc 'Return the user specific counts' do
detail 'Open MR Count'
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 73bb43b88fc..e7c1d644324 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Users < Grape::API::Instance
+ class Users < ::API::Base
include PaginationParams
include APIGuard
include Helpers::CustomAttributes
@@ -348,7 +348,7 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
- desc 'Get the GPG keys of a specified user. Available only for admins.' do
+ desc 'Get the GPG keys of a specified user.' do
detail 'This feature was added in GitLab 10.0'
success Entities::GpgKey
end
@@ -358,8 +358,6 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ':id/gpg_keys' do
- authenticated_as_admin!
-
user = User.find_by(id: params[:id])
not_found!('User') unless user
@@ -367,6 +365,26 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ desc 'Get a specific GPG key for a given user.' do
+ detail 'This feature was added in GitLab 13.5'
+ success Entities::GpgKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ':id/gpg_keys/:key_id' do
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ key = user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ present key, with: Entities::GpgKey
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
desc 'Delete an existing GPG key from a specified user. Available only for admins.' do
detail 'This feature was added in GitLab 10.0'
end
@@ -529,10 +547,15 @@ module API
unless user.can_be_deactivated?
forbidden!('A blocked user cannot be deactivated by the API') if user.blocked?
+ forbidden!('An internal user cannot be deactivated by the API') if user.internal?
forbidden!("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
end
- user.deactivate
+ if user.deactivate
+ true
+ else
+ render_api_error!(user.errors.full_messages, 400)
+ end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb
index 593f90460ac..aed88e6091c 100644
--- a/lib/api/v3/github.rb
+++ b/lib/api/v3/github.rb
@@ -7,7 +7,7 @@
#
module API
module V3
- class Github < Grape::API::Instance
+ class Github < ::API::Base
NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze
ENDPOINT_REQUIREMENTS = {
namespace: NO_SLASH_URL_PART_REGEX,
@@ -51,7 +51,7 @@ module API
def find_project_with_access(params)
project = find_project!(
- ::Gitlab::Jira::Dvcs.restore_full_path(params.slice(:namespace, :project).symbolize_keys)
+ ::Gitlab::Jira::Dvcs.restore_full_path(**params.slice(:namespace, :project).symbolize_keys)
)
not_found! unless can?(current_user, :download_code, project)
project
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index 0b3ec10f1b4..f5de3d844e6 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Variables < Grape::API::Instance
+ class Variables < ::API::Base
include PaginationParams
before { authenticate! }
diff --git a/lib/api/version.rb b/lib/api/version.rb
index 6a480fc2bd9..841b55f8d6c 100644
--- a/lib/api/version.rb
+++ b/lib/api/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Version < Grape::API::Instance
+ class Version < ::API::Base
helpers ::API::Helpers::GraphqlHelpers
include APIGuard
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index 4eba12157bd..21f457046f1 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module API
- class Wikis < Grape::API::Instance
+ class Wikis < ::API::Base
helpers ::API::Helpers::WikisHelpers
helpers do