summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/admin/batched_background_migrations.rb42
-rw-r--r--lib/api/admin/plan_limits.rb2
-rw-r--r--lib/api/alert_management_alerts.rb62
-rw-r--r--lib/api/api.rb71
-rw-r--r--lib/api/appearance.rb1
-rw-r--r--lib/api/award_emoji.rb2
-rw-r--r--lib/api/ci/job_artifacts.rb72
-rw-r--r--lib/api/ci/jobs.rb17
-rw-r--r--lib/api/ci/runner.rb14
-rw-r--r--lib/api/ci/runners.rb10
-rw-r--r--lib/api/ci/secure_files.rb47
-rw-r--r--lib/api/clusters/agent_tokens.rb9
-rw-r--r--lib/api/commit_statuses.rb14
-rw-r--r--lib/api/commits.rb25
-rw-r--r--lib/api/composer_packages.rb90
-rw-r--r--lib/api/conan_project_packages.rb2
-rw-r--r--lib/api/concerns/packages/conan_endpoints.rb168
-rw-r--r--lib/api/concerns/packages/debian_distribution_endpoints.rb80
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb90
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb92
-rw-r--r--lib/api/concerns/packages/nuget_endpoints.rb38
-rw-r--r--lib/api/container_registry_event.rb12
-rw-r--r--lib/api/container_repositories.rb7
-rw-r--r--lib/api/debian_group_packages.rb11
-rw-r--r--lib/api/debian_project_packages.rb33
-rw-r--r--lib/api/deployments.rb10
-rw-r--r--lib/api/entities/appearance.rb1
-rw-r--r--lib/api/entities/basic_success.rb12
-rw-r--r--lib/api/entities/batched_background_migration.rb12
-rw-r--r--lib/api/entities/ci/job_request/hook.rb13
-rw-r--r--lib/api/entities/ci/job_request/response.rb3
-rw-r--r--lib/api/entities/ci/runner_details.rb4
-rw-r--r--lib/api/entities/ci/secure_file.rb15
-rw-r--r--lib/api/entities/commit_signature.rb2
-rw-r--r--lib/api/entities/conan_package/conan_package_manifest.rb2
-rw-r--r--lib/api/entities/conan_package/conan_package_snapshot.rb6
-rw-r--r--lib/api/entities/conan_package/conan_recipe_manifest.rb2
-rw-r--r--lib/api/entities/conan_package/conan_recipe_snapshot.rb6
-rw-r--r--lib/api/entities/conan_package/conan_upload_urls.rb2
-rw-r--r--lib/api/entities/container_registry.rb23
-rw-r--r--lib/api/entities/event.rb16
-rw-r--r--lib/api/entities/issuable_references.rb6
-rw-r--r--lib/api/entities/issuable_time_stats.rb8
-rw-r--r--lib/api/entities/metric_image.rb8
-rw-r--r--lib/api/entities/milestone.rb2
-rw-r--r--lib/api/entities/ml/mlflow/experiment.rb1
-rw-r--r--lib/api/entities/ml/mlflow/key_value.rb (renamed from lib/api/entities/ml/mlflow/run_param.rb)2
-rw-r--r--lib/api/entities/ml/mlflow/run.rb3
-rw-r--r--lib/api/entities/namespace.rb2
-rw-r--r--lib/api/entities/namespace_basic.rb10
-rw-r--r--lib/api/entities/namespace_existence.rb3
-rw-r--r--lib/api/entities/npm_package.rb16
-rw-r--r--lib/api/entities/npm_package_tag.rb2
-rw-r--r--lib/api/entities/nuget/dependency.rb8
-rw-r--r--lib/api/entities/nuget/dependency_group.rb10
-rw-r--r--lib/api/entities/nuget/metadatum.rb6
-rw-r--r--lib/api/entities/nuget/package_metadata.rb7
-rw-r--r--lib/api/entities/nuget/package_metadata_catalog_entry.rb20
-rw-r--r--lib/api/entities/nuget/packages_metadata.rb5
-rw-r--r--lib/api/entities/nuget/packages_metadata_item.rb11
-rw-r--r--lib/api/entities/nuget/packages_versions.rb2
-rw-r--r--lib/api/entities/nuget/search_result.rb21
-rw-r--r--lib/api/entities/nuget/search_result_version.rb6
-rw-r--r--lib/api/entities/nuget/search_results.rb5
-rw-r--r--lib/api/entities/nuget/service_index.rb4
-rw-r--r--lib/api/entities/package.rb2
-rw-r--r--lib/api/entities/packages/debian/distribution.rb21
-rw-r--r--lib/api/entities/plan_limit.rb1
-rw-r--r--lib/api/entities/project.rb4
-rw-r--r--lib/api/entities/project_integration.rb4
-rw-r--r--lib/api/entities/push_event_payload.rb10
-rw-r--r--lib/api/entities/ssh_key.rb1
-rw-r--r--lib/api/entities/ssh_signature.rb10
-rw-r--r--lib/api/entities/tag_signature.rb13
-rw-r--r--lib/api/entities/todo.rb1
-rw-r--r--lib/api/events.rb14
-rw-r--r--lib/api/features.rb8
-rw-r--r--lib/api/files.rb16
-rw-r--r--lib/api/freeze_periods.rb2
-rw-r--r--lib/api/generic_packages.rb25
-rw-r--r--lib/api/group_debian_distributions.rb2
-rw-r--r--lib/api/groups.rb33
-rw-r--r--lib/api/helm_packages.rb44
-rw-r--r--lib/api/helpers.rb8
-rw-r--r--lib/api/helpers/award_emoji.rb22
-rw-r--r--lib/api/helpers/discussions_helpers.rb2
-rw-r--r--lib/api/helpers/integrations_helpers.rb9
-rw-r--r--lib/api/helpers/merge_requests_helpers.rb173
-rw-r--r--lib/api/helpers/notes_helpers.rb16
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb10
-rw-r--r--lib/api/helpers/packages/dependency_proxy_helpers.rb6
-rw-r--r--lib/api/helpers/packages_helpers.rb14
-rw-r--r--lib/api/helpers/projects_helpers.rb8
-rw-r--r--lib/api/integrations/jira_connect/subscriptions.rb14
-rw-r--r--lib/api/internal/base.rb2
-rw-r--r--lib/api/internal/kubernetes.rb11
-rw-r--r--lib/api/markdown.rb5
-rw-r--r--lib/api/maven_packages.rb68
-rw-r--r--lib/api/members.rb12
-rw-r--r--lib/api/merge_request_approvals.rb18
-rw-r--r--lib/api/merge_requests.rb320
-rw-r--r--lib/api/ml/mlflow.rb42
-rw-r--r--lib/api/namespaces.rb35
-rw-r--r--lib/api/npm_project_packages.rb16
-rw-r--r--lib/api/nuget_group_packages.rb2
-rw-r--r--lib/api/nuget_project_packages.rb75
-rw-r--r--lib/api/pages.rb11
-rw-r--r--lib/api/project_container_repositories.rb49
-rw-r--r--lib/api/project_packages.rb27
-rw-r--r--lib/api/project_snippets.rb2
-rw-r--r--lib/api/projects.rb270
-rw-r--r--lib/api/pypi_packages.rb85
-rw-r--r--lib/api/release/links.rb4
-rw-r--r--lib/api/rpm_project_packages.rb46
-rw-r--r--lib/api/rubygem_packages.rb49
-rw-r--r--lib/api/settings.rb8
-rw-r--r--lib/api/snippets.rb2
-rw-r--r--lib/api/support/git_access_actor.rb2
-rw-r--r--lib/api/tags.rb18
-rw-r--r--lib/api/terraform/state.rb29
-rw-r--r--lib/api/time_tracking_endpoints.rb78
-rw-r--r--lib/api/unleash.rb12
-rw-r--r--lib/api/usage_data.rb29
-rw-r--r--lib/api/usage_data_non_sql_metrics.rb6
-rw-r--r--lib/api/usage_data_queries.rb6
-rw-r--r--lib/api/users.rb26
-rw-r--r--lib/api/v3/github.rb4
-rw-r--r--lib/api/validations/validators/array_none_any.rb2
128 files changed, 2466 insertions, 706 deletions
diff --git a/lib/api/admin/batched_background_migrations.rb b/lib/api/admin/batched_background_migrations.rb
index e8cc08a23be..7e612b5b66a 100644
--- a/lib/api/admin/batched_background_migrations.rb
+++ b/lib/api/admin/batched_background_migrations.rb
@@ -12,7 +12,15 @@ module API
namespace 'admin' do
resources 'batched_background_migrations/:id' do
- desc 'Retrieve a batched background migration'
+ desc 'Retrieve a batched background migration' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' },
+ { code: 404, message: '404 Not found' }
+ ]
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
@@ -31,7 +39,15 @@ module API
end
resources 'batched_background_migrations' do
- desc 'Get the list of the batched background migrations'
+ desc 'Get the list of batched background migrations' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' }
+ ]
+ is_array true
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
@@ -48,7 +64,16 @@ module API
end
resources 'batched_background_migrations/:id/resume' do
- desc 'Resume a batched background migration'
+ desc 'Resume a batched background migration' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' },
+ { code: 404, message: '404 Not found' },
+ { code: 422, message: 'You can resume only `paused` batched background migrations.' }
+ ]
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
@@ -73,7 +98,16 @@ module API
end
resources 'batched_background_migrations/:id/pause' do
- desc 'Pause a batched background migration'
+ desc 'Pause a batched background migration' do
+ success ::API::Entities::BatchedBackgroundMigration
+ failure [
+ { code: 401, message: '401 Unauthorized' },
+ { code: 403, message: '403 Forbidden' },
+ { code: 404, message: '404 Not found' },
+ { code: 422, message: 'You can pause only `active` batched background migrations.' }
+ ]
+ tags %w[batched_background_migrations]
+ end
params do
optional :database,
type: String,
diff --git a/lib/api/admin/plan_limits.rb b/lib/api/admin/plan_limits.rb
index 49b41b44a18..5ef56d3326f 100644
--- a/lib/api/admin/plan_limits.rb
+++ b/lib/api/admin/plan_limits.rb
@@ -70,6 +70,8 @@ module API
optional :terraform_module_max_file_size, type: Integer,
desc: 'Maximum Terraform Module package file size in bytes'
optional :storage_size_limit, type: Integer, desc: 'Maximum storage size for the root namespace in megabytes'
+ optional :pipeline_hierarchy_size, type: Integer,
+ desc: "Maximum number of downstream pipelines in a pipeline's hierarchy tree"
end
put "application/plan_limits" do
params = declared_params(include_missing: false)
diff --git a/lib/api/alert_management_alerts.rb b/lib/api/alert_management_alerts.rb
index f57b7d00c81..9e28ee049d0 100644
--- a/lib/api/alert_management_alerts.rb
+++ b/lib/api/alert_management_alerts.rb
@@ -6,12 +6,21 @@ module API
urgency :low
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
- requires :alert_iid, type: Integer, desc: 'The IID of the Alert'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project',
+ documentation: { example: 17 }
+ requires :alert_iid, type: Integer, desc: 'The IID of the Alert',
+ documentation: { example: 23 }
end
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/alert_management_alerts/:alert_iid/metric_images' do
+ desc 'Workhorse authorize metric image file upload' do
+ success code: 200
+ failure [
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[alert_management]
+ end
post 'authorize' do
authorize!(:upload_alert_management_metric_image, find_project_alert(request.params[:alert_iid]))
@@ -29,13 +38,20 @@ module API
end
desc 'Upload a metric image for an alert' do
- success Entities::MetricImage
+ consumes ['multipart/form-data']
+ success code: 200, model: Entities::MetricImage
+ failure [
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[alert_management]
end
params do
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The image file to be uploaded',
documentation: { type: 'file' }
- optional :url, type: String, desc: 'The url to view more metric info'
- optional :url_text, type: String, desc: 'A description of the image or URL'
+ optional :url, type: String, desc: 'The url to view more metric info',
+ documentation: { example: 'https://example.com/metric' }
+ optional :url_text, type: String, desc: 'A description of the image or URL',
+ documentation: { example: 'An example metric' }
end
post do
require_gitlab_workhorse!
@@ -61,7 +77,14 @@ module API
end
end
- desc 'Metric Images for alert'
+ desc 'Metric Images for alert' do
+ success code: 200, model: Entities::MetricImage
+ is_array true
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[alert_management]
+ end
get do
alert = find_project_alert(params[:alert_iid])
@@ -73,12 +96,21 @@ module API
end
desc 'Update a metric image for an alert' do
- success Entities::MetricImage
+ consumes ['multipart/form-data']
+ success code: 200, model: Entities::MetricImage
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[alert_management]
end
params do
- requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
- optional :url, type: String, desc: 'The url to view more metric info'
- optional :url_text, type: String, desc: 'A description of the image or URL'
+ requires :metric_image_id, type: Integer, desc: 'The ID of metric image',
+ documentation: { example: 42 }
+ optional :url, type: String, desc: 'The url to view more metric info',
+ documentation: { example: 'https://example.com/metric' }
+ optional :url_text, type: String, desc: 'A description of the image or URL',
+ documentation: { example: 'An example metric' }
end
put ':metric_image_id' do
alert = find_project_alert(params[:alert_iid])
@@ -97,10 +129,16 @@ module API
end
desc 'Remove a metric image for an alert' do
- success Entities::MetricImage
+ success code: 204, model: Entities::MetricImage
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[alert_management]
end
params do
- requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
+ requires :metric_image_id, type: Integer, desc: 'The ID of metric image',
+ documentation: { example: 42 }
end
delete ':metric_image_id' do
alert = find_project_alert(params[:alert_iid])
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ffb0cdf8991..b23b11d0c29 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -171,9 +171,11 @@ module API
namespace do
# Keep in alphabetical order
mount ::API::AccessRequests
+ mount ::API::Admin::BatchedBackgroundMigrations
mount ::API::Admin::Ci::Variables
mount ::API::Admin::InstanceClusters
mount ::API::Admin::PlanLimits
+ mount ::API::AlertManagementAlerts
mount ::API::Appearance
mount ::API::Applications
mount ::API::Avatar
@@ -181,10 +183,13 @@ module API
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::BulkImports
+ mount ::API::Ci::JobArtifacts
+ mount ::API::Groups
mount ::API::Ci::Jobs
mount ::API::Ci::ResourceGroups
mount ::API::Ci::Runner
mount ::API::Ci::Runners
+ mount ::API::Ci::SecureFiles
mount ::API::Ci::Pipelines
mount ::API::Ci::PipelineSchedules
mount ::API::Ci::Triggers
@@ -193,6 +198,13 @@ module API
mount ::API::Clusters::Agents
mount ::API::Commits
mount ::API::CommitStatuses
+ mount ::API::ComposerPackages
+ mount ::API::ConanInstancePackages
+ mount ::API::ConanProjectPackages
+ mount ::API::ContainerRegistryEvent
+ mount ::API::ContainerRepositories
+ mount ::API::DebianGroupPackages
+ mount ::API::DebianProjectPackages
mount ::API::DependencyProxy
mount ::API::DeployKeys
mount ::API::DeployTokens
@@ -200,54 +212,75 @@ module API
mount ::API::Environments
mount ::API::ErrorTracking::ClientKeys
mount ::API::ErrorTracking::ProjectSettings
+ mount ::API::Events
mount ::API::FeatureFlags
mount ::API::FeatureFlagsUserLists
mount ::API::Features
mount ::API::Files
mount ::API::FreezePeriods
+ mount ::API::GenericPackages
mount ::API::Geo
mount ::API::GoProxy
mount ::API::GroupAvatar
mount ::API::GroupClusters
mount ::API::GroupContainerRepositories
+ mount ::API::GroupDebianDistributions
mount ::API::GroupExport
mount ::API::GroupImport
mount ::API::GroupPackages
mount ::API::GroupVariables
+ mount ::API::HelmPackages
mount ::API::ImportBitbucketServer
mount ::API::ImportGithub
mount ::API::Integrations
+ mount ::API::Integrations::JiraConnect::Subscriptions
mount ::API::Invitations
mount ::API::IssueLinks
mount ::API::Keys
mount ::API::Lint
mount ::API::Markdown
+ mount ::API::MavenPackages
+ mount ::API::Members
mount ::API::MergeRequestApprovals
+ mount ::API::MergeRequests
mount ::API::MergeRequestDiffs
mount ::API::Metadata
mount ::API::Metrics::Dashboard::Annotations
mount ::API::Metrics::UserStarredDashboards
+ mount ::API::Namespaces
+ mount ::API::NpmInstancePackages
+ mount ::API::NpmProjectPackages
+ mount ::API::NugetGroupPackages
+ mount ::API::NugetProjectPackages
mount ::API::PackageFiles
+ mount ::API::Pages
mount ::API::PersonalAccessTokens::SelfInformation
mount ::API::PersonalAccessTokens
mount ::API::ProjectClusters
+ mount ::API::ProjectContainerRepositories
+ mount ::API::ProjectDebianDistributions
mount ::API::ProjectEvents
mount ::API::ProjectExport
mount ::API::ProjectHooks
mount ::API::ProjectImport
+ mount ::API::ProjectPackages
mount ::API::ProjectRepositoryStorageMoves
mount ::API::ProjectSnippets
mount ::API::ProjectSnapshots
mount ::API::ProjectStatistics
mount ::API::ProjectTemplates
+ mount ::API::Projects
mount ::API::ProtectedBranches
mount ::API::ProtectedTags
+ mount ::API::PypiPackages
mount ::API::Releases
mount ::API::Release::Links
mount ::API::RemoteMirrors
mount ::API::Repositories
mount ::API::ResourceAccessTokens
mount ::API::ResourceMilestoneEvents
+ mount ::API::RpmProjectPackages
+ mount ::API::RubygemPackages
mount ::API::Snippets
mount ::API::SnippetRepositoryStorageMoves
mount ::API::Statistics
@@ -260,6 +293,9 @@ module API
mount ::API::Terraform::StateVersion
mount ::API::Topics
mount ::API::Unleash
+ mount ::API::UsageData
+ mount ::API::UsageDataNonSqlMetrics
+ mount ::API::UsageDataQueries
mount ::API::UserCounts
mount ::API::Wikis
@@ -267,57 +303,27 @@ module API
end
# Keep in alphabetical order
- mount ::API::Admin::BatchedBackgroundMigrations
mount ::API::Admin::Sidekiq
- mount ::API::AlertManagementAlerts
mount ::API::AwardEmoji
mount ::API::Boards
- mount ::API::Ci::JobArtifacts
+ mount ::API::Ci::Pipelines
+ mount ::API::Ci::PipelineSchedules
mount ::API::Ci::SecureFiles
- mount ::API::ComposerPackages
- mount ::API::ConanInstancePackages
- mount ::API::ConanProjectPackages
- mount ::API::ContainerRegistryEvent
- mount ::API::ContainerRepositories
- mount ::API::DebianGroupPackages
- mount ::API::DebianProjectPackages
mount ::API::Discussions
mount ::API::ErrorTracking::Collector
- mount ::API::Events
- mount ::API::GenericPackages
mount ::API::GroupBoards
- mount ::API::GroupDebianDistributions
mount ::API::GroupLabels
mount ::API::GroupMilestones
- mount ::API::Groups
- mount ::API::HelmPackages
- mount ::API::Integrations::JiraConnect::Subscriptions
mount ::API::Issues
mount ::API::Labels
- mount ::API::MavenPackages
- mount ::API::Members
- mount ::API::MergeRequests
- mount ::API::Namespaces
mount ::API::Notes
mount ::API::NotificationSettings
- mount ::API::NpmInstancePackages
- mount ::API::NpmProjectPackages
- mount ::API::NugetGroupPackages
- mount ::API::NugetProjectPackages
- mount ::API::Pages
mount ::API::PagesDomains
- mount ::API::ProjectContainerRepositories
- mount ::API::ProjectDebianDistributions
mount ::API::ProjectEvents
mount ::API::ProjectMilestones
- mount ::API::ProjectPackages
- mount ::API::Projects
mount ::API::ProtectedTags
- mount ::API::PypiPackages
mount ::API::ResourceLabelEvents
mount ::API::ResourceStateEvents
- mount ::API::RpmProjectPackages
- mount ::API::RubygemPackages
mount ::API::Search
mount ::API::Settings
mount ::API::SidekiqMetrics
@@ -327,7 +333,6 @@ module API
mount ::API::Todos
mount ::API::UsageData
mount ::API::UsageDataNonSqlMetrics
- mount ::API::UsageDataQueries
mount ::API::Users
mount ::API::Ml::Mlflow
end
diff --git a/lib/api/appearance.rb b/lib/api/appearance.rb
index 69f1521ef2a..2cef1b27504 100644
--- a/lib/api/appearance.rb
+++ b/lib/api/appearance.rb
@@ -26,6 +26,7 @@ module API
end
params do
optional :title, type: String, desc: 'Instance title on the sign in / sign up page'
+ optional :short_title, type: String, desc: 'Short title for Progressive Web App'
optional :description, type: String, desc: 'Markdown text shown on the sign in / sign up page'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :logo, type: File, desc: 'Instance image used on the sign in / sign up page' # rubocop:disable Scalability/FileUploads
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index e419a025508..f7a39db7249 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -80,7 +80,7 @@ module API
delete "#{endpoint}/:award_id", feature_category: awardable_params[:feature_category] do
award = awardable.award_emoji.find(params[:award_id])
- unauthorized! unless award.user == current_user || current_user&.admin?
+ unauthorized! unless award.user == current_user || current_user&.can_admin_all_resources?
destroy_conditionally!(award)
end
diff --git a/lib/api/ci/job_artifacts.rb b/lib/api/ci/job_artifacts.rb
index 352ad04c982..3788f5bec41 100644
--- a/lib/api/ci/job_artifacts.rb
+++ b/lib/api/ci/job_artifacts.rb
@@ -16,18 +16,25 @@ module API
end
end
- prepend_mod_with('API::Ci::JobArtifacts') # rubocop: disable Cop/InjectEnterpriseEditionModule
-
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Download the artifacts archive from a job' do
detail 'This feature was introduced in GitLab 8.10'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :ref_name, type: String, desc: 'The ref from repository'
- requires :job, type: String, desc: 'The name for the job'
+ requires :ref_name, type: String,
+ desc: 'Branch or tag name in repository. `HEAD` or `SHA` references are not supported.'
+ requires :job, type: String, desc: 'The name of the job.'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/artifacts/:ref_name/download',
@@ -43,11 +50,21 @@ module API
desc 'Download a specific file from artifacts archive from a ref' do
detail 'This feature was introduced in GitLab 11.5'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :ref_name, type: String, desc: 'The ref from repository'
- requires :job, type: String, desc: 'The name for the job'
- requires :artifact_path, type: String, desc: 'Artifact path'
+ requires :ref_name, type: String,
+ desc: 'Branch or tag name in repository. `HEAD` or `SHA` references are not supported.'
+ requires :job, type: String, desc: 'The name of the job.'
+ requires :artifact_path, type: String, desc: 'Path to a file inside the artifacts archive.'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/artifacts/:ref_name/raw/*artifact_path',
@@ -69,9 +86,17 @@ module API
desc 'Download the artifacts archive from a job' do
detail 'This feature was introduced in GitLab 8.5'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/:job_id/artifacts', urgency: :low do
@@ -85,10 +110,19 @@ module API
desc 'Download a specific file from artifacts archive' do
detail 'This feature was introduced in GitLab 10.0'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
- requires :artifact_path, type: String, desc: 'Artifact path'
+ requires :artifact_path, type: String, desc: 'Path to a file inside the artifacts archive.'
+ optional :job_token, type: String,
+ desc: 'To be used with triggers for multi-project pipelines, ' \
+ 'available only on Premium and Ultimate tiers.'
end
route_setting :authentication, job_token_allowed: true
get ':id/jobs/:job_id/artifacts/*artifact_path', urgency: :low, format: false do
@@ -113,6 +147,11 @@ module API
desc 'Keep the artifacts to prevent them from being deleted' do
success ::API::Entities::Ci::Job
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
@@ -132,6 +171,12 @@ module API
desc 'Delete the artifacts files from a job' do
detail 'This feature was introduced in GitLab 11.9'
+ success code: 204
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 409, message: 'Conflict' }
+ ]
end
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
@@ -148,7 +193,14 @@ module API
status :no_content
end
- desc 'Expire the artifacts files from a project'
+ desc 'Expire the artifacts files from a project' do
+ success code: 202
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 409, message: 'Conflict' }
+ ]
+ end
delete ':id/artifacts' do
authorize_destroy_artifacts!
@@ -162,3 +214,5 @@ module API
end
end
end
+
+API::Ci::JobArtifacts.prepend_mod
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index 9e41e1c0d8f..bb57a717f7c 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -49,13 +49,15 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ':id/jobs', urgency: :low, feature_category: :continuous_integration do
+ check_rate_limit!(:jobs_index, scope: current_user) if enforce_jobs_api_rate_limits(@project)
+
authorize_read_builds!
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
-
builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project)
- present paginate(builds), with: Entities::Ci::Job
+
+ present paginate(builds, without_count: true), with: Entities::Ci::Job
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -255,16 +257,19 @@ module API
pipeline = current_authenticated_job.pipeline
project = current_authenticated_job.project
- agent_authorizations = ::Clusters::AgentAuthorizationsFinder.new(project).execute
project_groups = project.group&.self_and_ancestor_ids&.map { |id| { id: id } } || []
user_access_level = project.team.max_member_access(current_user.id)
roles_in_project = Gitlab::Access.sym_options_with_owner
.select { |_role, role_access_level| role_access_level <= user_access_level }
.map(&:first)
- environment = if persisted_environment = current_authenticated_job.actual_persisted_environment
- { tier: persisted_environment.tier, slug: persisted_environment.slug }
- end
+ persisted_environment = current_authenticated_job.actual_persisted_environment
+ environment = { tier: persisted_environment.tier, slug: persisted_environment.slug } if persisted_environment
+
+ agent_authorizations = ::Clusters::Agents::FilterAuthorizationsService.new(
+ ::Clusters::AgentAuthorizationsFinder.new(project).execute,
+ environment: persisted_environment&.name
+ ).execute
# See https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/kubernetes_ci_access.md#apiv4joballowed_agents-api
{
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index c7d1887638a..b073eb49bf1 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -229,15 +229,17 @@ module API
params do
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
+ optional :debug_trace, type: Boolean, desc: %q(Enable or Disable the debug trace)
end
patch '/:id/trace', urgency: :low, feature_category: :continuous_integration do
job = authenticate_job!(heartbeat_runner: true)
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
content_range = request.headers['Content-Range']
+ debug_trace = Gitlab::Utils.to_boolean(params[:debug_trace])
result = ::Ci::AppendBuildTraceService
- .new(job, content_range: content_range)
+ .new(job, content_range: content_range, debug_trace: debug_trace)
.execute(request.body.read)
if result.status == 403
@@ -256,7 +258,7 @@ module API
header 'X-GitLab-Trace-Update-Interval', job.trace.update_interval.to_s
end
- desc 'Authorize artifacts uploading for job' do
+ desc 'Authorize uploading job artifact' do
http_codes [[200, 'Upload allowed'],
[403, 'Forbidden'],
[405, 'Artifacts support not enabled'],
@@ -270,7 +272,7 @@ module API
# In current runner, filesize parameter would be empty here. This is because archive is streamed by runner,
# so the archive size is not known ahead of time. Streaming is done to not use additional I/O on
# Runner to first save, and then send via Network.
- optional :filesize, type: Integer, desc: %q(Artifacts filesize)
+ optional :filesize, type: Integer, desc: %q(Size of artifact file)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: ::Ci::JobArtifact.file_types.keys
@@ -292,7 +294,7 @@ module API
end
end
- desc 'Upload artifacts for job' do
+ desc 'Upload a job artifact' do
success Entities::Ci::JobRequest::Response
http_codes [[201, 'Artifact uploaded'],
[400, 'Bad request'],
@@ -304,7 +306,7 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: %(The artifact file to store (generated by Multipart middleware)), documentation: { type: 'file' }
optional :token, type: String, desc: %q(Job's authentication token)
- optional :expire_in, type: String, desc: %q(Specify when artifacts should expire)
+ optional :expire_in, type: String, desc: %q(Specify when artifact should expire)
optional :artifact_type, type: String, desc: %q(The type of artifact),
default: 'archive', values: ::Ci::JobArtifact.file_types.keys
optional :artifact_format, type: String, desc: %q(The format of artifact),
@@ -333,7 +335,7 @@ module API
end
desc 'Download the artifacts file for job' do
- http_codes [[200, 'Upload allowed'],
+ http_codes [[200, 'Download allowed'],
[401, 'Unauthorized'],
[403, 'Forbidden'],
[404, 'Artifact not found']]
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 988c3f4f566..4a6c58b4987 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -58,19 +58,19 @@ module API
end
def authenticate_show_runner!(runner)
- return if runner.instance_type? || current_user.admin?
+ return if runner.instance_type? || current_user.can_read_all_resources?
forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
end
def authenticate_update_runner!(runner)
- return if current_user.admin?
+ return if current_user.can_admin_all_resources?
forbidden!("No access granted") unless can?(current_user, :update_runner, runner)
end
def authenticate_delete_runner!(runner)
- return if current_user.admin?
+ return if current_user.can_admin_all_resources?
forbidden!("Runner associated with more than one project") if runner.runner_projects.count > 1
forbidden!("No access granted") unless can?(current_user, :delete_runner, runner)
@@ -79,14 +79,14 @@ module API
def authenticate_enable_runner!(runner)
forbidden!("Runner is a group runner") if runner.group_type?
- return if current_user.admin?
+ return if current_user.can_admin_all_resources?
forbidden!("Runner is locked") if runner.locked?
forbidden!("No access granted") unless can?(current_user, :assign_runner, runner)
end
def authenticate_list_runners_jobs!(runner)
- return if current_user.admin?
+ return if current_user.can_read_all_resources?
forbidden!("No access granted") unless can?(current_user, :read_builds, runner)
end
diff --git a/lib/api/ci/secure_files.rb b/lib/api/ci/secure_files.rb
index dd628a3413f..6483abcc74e 100644
--- a/lib/api/ci/secure_files.rb
+++ b/lib/api/ci/secure_files.rb
@@ -7,7 +7,6 @@ module API
before do
authenticate!
- feature_flag_enabled?
authorize! :read_secure_files, user_project
end
@@ -16,11 +15,15 @@ module API
default_format :json
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project owned by the
+ authenticated user'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'List all Secure Files for a Project'
+ desc 'Get list of secure files in a project' do
+ success Entities::Ci::SecureFile
+ tags %w[secure_files]
+ end
params do
use :pagination
end
@@ -30,9 +33,13 @@ module API
present paginate(secure_files), with: Entities::Ci::SecureFile
end
- desc 'Get an individual Secure File'
+ desc 'Get the details of a specific secure file in a project' do
+ success Entities::Ci::SecureFile
+ tags %w[secure_files]
+ failure [{ code: 404, message: '404 Not found' }]
+ end
params do
- requires :id, type: Integer, desc: 'The Secure File ID'
+ requires :id, type: Integer, desc: 'The ID of a secure file'
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
@@ -41,7 +48,10 @@ module API
present secure_file, with: Entities::Ci::SecureFile
end
- desc 'Download a Secure File'
+ desc 'Download secure file' do
+ failure [{ code: 404, message: '404 Not found' }]
+ tags %w[secure_files]
+ end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
get ':id/secure_files/:secure_file_id/download' do
secure_file = user_project.secure_files.find(params[:secure_file_id])
@@ -58,10 +68,15 @@ module API
authorize! :admin_secure_files, user_project
end
- desc 'Upload a Secure File'
+ desc 'Create a secure file' do
+ success Entities::Ci::SecureFile
+ tags %w[secure_files]
+ failure [{ code: 400, message: '400 Bad Request' }]
+ end
params do
- requires :name, type: String, desc: 'The name of the file'
- requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file to be uploaded', documentation: { type: 'file' }
+ requires :name, type: String, desc: 'The name of the file being uploaded. The filename must be unique within
+ the project'
+ requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file being uploaded', documentation: { type: 'file' }
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
post ':id/secure_files' do
@@ -74,17 +89,17 @@ module API
file_too_large! unless secure_file.file.size < ::Ci::SecureFile::FILE_SIZE_LIMIT.to_i
if secure_file.save
- if Feature.enabled?(:secure_files_metadata_parsers, user_project)
- ::Ci::ParseSecureFileMetadataWorker.perform_async(secure_file.id) # rubocop:disable CodeReuse/Worker
- end
-
+ ::Ci::ParseSecureFileMetadataWorker.perform_async(secure_file.id) # rubocop:disable CodeReuse/Worker
present secure_file, with: Entities::Ci::SecureFile
else
render_validation_error!(secure_file)
end
end
- desc 'Delete an individual Secure File'
+ desc 'Remove a secure file' do
+ tags %w[secure_files]
+ failure [{ code: 404, message: '404 Not found' }]
+ end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
delete ':id/secure_files/:secure_file_id' do
secure_file = user_project.secure_files.find(params[:secure_file_id])
@@ -97,10 +112,6 @@ module API
end
helpers do
- def feature_flag_enabled?
- service_unavailable! unless Feature.enabled?(:ci_secure_files, user_project)
- end
-
def read_only_feature_flag_enabled?
service_unavailable! if Feature.enabled?(:ci_secure_files_read_only, user_project, type: :ops)
end
diff --git a/lib/api/clusters/agent_tokens.rb b/lib/api/clusters/agent_tokens.rb
index f65ae465b3d..68eef21903d 100644
--- a/lib/api/clusters/agent_tokens.rb
+++ b/lib/api/clusters/agent_tokens.rb
@@ -27,7 +27,8 @@ module API
use :pagination
end
get do
- agent_tokens = ::Clusters::AgentTokensFinder.new(user_project, current_user, params[:agent_id]).execute
+ agent = ::Clusters::AgentsFinder.new(user_project, current_user).find(params[:agent_id])
+ agent_tokens = ::Clusters::AgentTokensFinder.new(agent, current_user).execute
present paginate(agent_tokens), with: Entities::Clusters::AgentTokenBasic
end
@@ -42,8 +43,7 @@ module API
end
get ':token_id' do
agent = ::Clusters::AgentsFinder.new(user_project, current_user).find(params[:agent_id])
-
- token = agent.agent_tokens.find(params[:token_id])
+ token = ::Clusters::AgentTokensFinder.new(agent, current_user).find(params[:token_id])
present token, with: Entities::Clusters::AgentToken
end
@@ -84,8 +84,7 @@ module API
authorize! :admin_cluster, user_project
agent = ::Clusters::AgentsFinder.new(user_project, current_user).find(params[:agent_id])
-
- token = agent.agent_tokens.find(params[:token_id])
+ token = ::Clusters::AgentTokensFinder.new(agent, current_user).find(params[:token_id])
# Skipping explicit error handling and relying on exceptions
token.revoked!
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 954b572c9b1..531235dc9b2 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -110,13 +110,23 @@ module API
authorize! :update_pipeline, pipeline
+ # rubocop: disable Performance/ActiveRecordSubtransactionMethods
+ stage = pipeline.stages.safe_find_or_create_by!(name: 'external') do |stage|
+ stage.position = GenericCommitStatus::EXTERNAL_STAGE_IDX
+ stage.project = pipeline.project
+ end
+ # rubocop: enable Performance/ActiveRecordSubtransactionMethods
+
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
project: user_project,
pipeline: pipeline,
name: name,
ref: ref,
user: current_user,
- protected: user_project.protected_for?(ref)
+ protected: user_project.protected_for?(ref),
+ ci_stage: stage,
+ stage_idx: stage.position,
+ stage: 'external'
)
updatable_optional_attributes = %w[target_url description coverage]
@@ -152,7 +162,7 @@ module API
def all_matching_pipelines
pipelines = user_project.ci_pipelines.newest_first(sha: commit.sha)
pipelines = pipelines.for_ref(params[:ref]) if params[:ref]
- pipelines = pipelines.for_id(params[:pipeline_id]) if params[:pipeline_id]
+ pipelines = pipelines.id_in(params[:pipeline_id]) if params[:pipeline_id]
pipelines
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 63a13b83a9b..ad2bbf90917 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -24,6 +24,26 @@ module API
forbidden!("You are not allowed to push into this branch")
end
end
+
+ def track_commit_events
+ return unless find_user_from_warden
+
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count
+ Gitlab::UsageDataCounters::EditorUniqueCounter.track_web_ide_edit_action(author: current_user, project: user_project)
+ namespace = user_project.namespace
+
+ return unless Feature.enabled?(:route_hll_to_snowplow_phase3, namespace)
+
+ Gitlab::Tracking.event(
+ 'API::Commits',
+ :commit,
+ project: user_project,
+ namespace: namespace,
+ user: current_user,
+ label: 'counts.web_ide_commits',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, key_path: 'counts.web_ide_commits').to_context]
+ )
+ end
end
params do
@@ -204,10 +224,7 @@ module API
if result[:status] == :success
commit_detail = user_project.repository.commit(result[:result])
- if find_user_from_warden
- Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count
- Gitlab::UsageDataCounters::EditorUniqueCounter.track_web_ide_edit_action(author: current_user, project: user_project)
- end
+ track_commit_events
present commit_detail, with: Entities::CommitDetail, include_stats: params[:stats], current_user: current_user
else
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index d9806fa37d1..3819e6d236d 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -61,32 +61,56 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of a group'
end
resource :group, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before do
+ after_validation do
user_group
end
- desc 'Composer packages endpoint at group level'
+ desc 'Composer packages endpoint at group level' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/packages', urgency: :low do
presenter.root
end
- desc 'Composer packages endpoint at group level for packages list'
+ desc 'Composer packages endpoint at group level for packages list' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :sha, type: String, desc: 'Shasum of current json'
+ requires :sha, type: String, desc: 'Shasum of current json', documentation: { example: '673594f85a55fe3c0eb45df7bd2fa9d95a1601ab' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/p/:sha', urgency: :low do
presenter.provider
end
- desc 'Composer v2 packages p2 endpoint at group level for package versions metadata'
+ desc 'Composer v2 packages p2 endpoint at group level for package versions metadata' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
+ requires :package_name, type: String, file_path: true, desc: 'The Composer package name', documentation: { example: 'my-composer-package' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/p2/*package_name', requirements: COMPOSER_ENDPOINT_REQUIREMENTS, file_path: true, urgency: :low do
@@ -95,9 +119,17 @@ module API
presenter.package_versions
end
- desc 'Composer packages endpoint at group level for package versions metadata'
+ desc 'Composer packages endpoint at group level for package versions metadata' do
+ detail 'This feature was introduced in GitLab 12.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
+ requires :package_name, type: String, file_path: true, desc: 'The Composer package name', documentation: { example: 'my-composer-package' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get ':id/-/packages/composer/*package_name', requirements: COMPOSER_ENDPOINT_REQUIREMENTS, file_path: true, urgency: :low do
@@ -109,17 +141,27 @@ module API
end
params do
- requires :id, type: Integer, desc: 'The ID of a project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Composer packages endpoint for registering packages'
namespace ':id/packages/composer' do
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
+ desc 'Composer packages endpoint for registering packages' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- optional :branch, type: String, desc: 'The name of the branch'
- optional :tag, type: String, desc: 'The name of the tag'
+ optional :branch, type: String, desc: 'The name of the branch', documentation: { example: 'release' }
+ optional :tag, type: String, desc: 'The name of the tag', documentation: { example: 'v1.0.0' }
exactly_one_of :tag, :branch
end
post urgency: :low do
@@ -142,15 +184,25 @@ module API
created!
end
+ desc 'Composer package endpoint to download a package archive' do
+ detail 'This feature was introduced in GitLab 13.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[composer_packages]
+ end
params do
- requires :sha, type: String, desc: 'Shasum of current json'
- requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
+ requires :sha, type: String, desc: 'Shasum of current json', documentation: { example: '673594f85a55fe3c0eb45df7bd2fa9d95a1601ab' }
+ requires :package_name, type: String, file_path: true, desc: 'The Composer package name', documentation: { example: 'my-composer-package' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get 'archives/*package_name', urgency: :default do
- authorize_read_package!(authorized_user_project)
+ project = authorized_user_project(action: :read_package)
- package = authorized_user_project
+ package = project
.packages
.composer
.with_name(params[:package_name])
@@ -160,10 +212,10 @@ module API
not_found! unless metadata
- track_package_event('pull_package', :composer, project: authorized_user_project, namespace: authorized_user_project.namespace)
+ track_package_event('pull_package', :composer, project: project, namespace: project.namespace)
package.touch_last_downloaded_at
- send_git_archive authorized_user_project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
+ send_git_archive project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
end
end
end
diff --git a/lib/api/conan_project_packages.rb b/lib/api/conan_project_packages.rb
index 636b5dca5ed..e282443e85c 100644
--- a/lib/api/conan_project_packages.rb
+++ b/lib/api/conan_project_packages.rb
@@ -4,7 +4,7 @@
module API
class ConanProjectPackages < ::API::Base
params do
- requires :id, type: Integer, desc: 'The ID of a project', regexp: %r{\A[1-9]\d*\z}
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb
index fdbffb1689b..e65e8f8710c 100644
--- a/lib/api/concerns/packages/conan_endpoints.rb
+++ b/lib/api/concerns/packages/conan_endpoints.rb
@@ -53,6 +53,11 @@ module API
desc 'Ping the Conan API' do
detail 'This feature was introduced in GitLab 12.2'
+ success code: 200
+ failure [
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -63,10 +68,15 @@ module API
desc 'Search for packages' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200
+ failure [
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :q, type: String, desc: 'Search query'
+ requires :q, type: String, desc: 'Search query', documentation: { example: 'Hello*' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -86,6 +96,12 @@ module API
desc 'Authenticate user against conan CLI' do
detail 'This feature was introduced in GitLab 12.2'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -98,6 +114,12 @@ module API
desc 'Check for valid user credentials per conan CLI' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -109,10 +131,10 @@ module API
end
params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel'
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name', documentation: { example: 'my-package' }
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version', documentation: { example: '1.0' }
+ requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username', documentation: { example: 'my-group+my-project' }
+ requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel', documentation: { example: 'stable' }
end
namespace 'conans/:package_name/:package_version/:package_username/:package_channel', requirements: PACKAGE_REQUIREMENTS do
after_validation do
@@ -122,14 +144,21 @@ module API
# Get the snapshot
#
# the snapshot is a hash of { filename: md5 hash }
- # md5 hash is the has of that file. This hash is used to diff the files existing on the client
+ # md5 hash is the hash of that file. This hash is used to diff the files existing on the client
# to determine which client files need to be uploaded if no recipe exists the snapshot is empty
desc 'Package Snapshot' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanPackageSnapshot
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -149,6 +178,13 @@ module API
desc 'Recipe Snapshot' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanRecipeSnapshot
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -168,9 +204,16 @@ module API
# where the url is the download url for the file
desc 'Package Digest' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanPackageManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -181,6 +224,13 @@ module API
desc 'Recipe Digest' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanRecipeManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -197,10 +247,17 @@ module API
# where the url is the download url for the file
desc 'Package Download Urls' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanPackageManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -211,6 +268,13 @@ module API
desc 'Recipe Download Urls' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanRecipeManifest
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -228,10 +292,17 @@ module API
# where the url is the upload url for the file that the conan client will use
desc 'Package Upload Urls' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanUploadUrls
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ requires :conan_package_reference, type: String, desc: 'Conan package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -245,6 +316,13 @@ module API
desc 'Recipe Upload Urls' do
detail 'This feature was introduced in GitLab 12.4'
+ success code: 200, model: ::API::Entities::ConanPackage::ConanUploadUrls
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -258,6 +336,13 @@ module API
desc 'Delete Package' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -272,11 +357,11 @@ module API
end
params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel'
- requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision'
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name', documentation: { example: 'my-package' }
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version', documentation: { example: '1.0' }
+ requires :package_username, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package username', documentation: { example: 'my-group+my-project' }
+ requires :package_channel, type: String, regexp: CONAN_REVISION_USER_CHANNEL_REGEX, desc: 'Package channel', documentation: { example: 'stable' }
+ requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision', documentation: { example: '0' }
end
namespace 'files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision', requirements: PACKAGE_REQUIREMENTS do
before do
@@ -288,12 +373,19 @@ module API
end
params do
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES, documentation: { example: 'conanfile.py' }
end
namespace 'export/:file_name', requirements: FILE_NAME_REQUIREMENTS do
desc 'Download recipe files' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -304,6 +396,14 @@ module API
desc 'Upload recipe package files' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
@@ -318,6 +418,14 @@ module API
desc 'Workhorse authorize the conan recipe file' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -328,13 +436,19 @@ module API
end
params do
- requires :conan_package_reference, type: String, desc: 'Conan Package ID'
- requires :package_revision, type: String, desc: 'Conan Package Revision'
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ requires :conan_package_reference, type: String, desc: 'Conan Package ID', documentation: { example: '103f6067a947f366ef91fc1b7da351c588d1827f' }
+ requires :package_revision, type: String, desc: 'Conan Package Revision', documentation: { example: '0' }
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES, documentation: { example: 'conaninfo.txt' }
end
namespace 'package/:conan_package_reference/:package_revision/:file_name', requirements: FILE_NAME_REQUIREMENTS do
desc 'Download package files' do
detail 'This feature was introduced in GitLab 12.5'
+ success code: 200
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -345,6 +459,14 @@ module API
desc 'Workhorse authorize the conan package file' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
@@ -355,6 +477,14 @@ module API
desc 'Upload package files' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
end
params do
diff --git a/lib/api/concerns/packages/debian_distribution_endpoints.rb b/lib/api/concerns/packages/debian_distribution_endpoints.rb
index 380966136df..76b996f2301 100644
--- a/lib/api/concerns/packages/debian_distribution_endpoints.rb
+++ b/lib/api/concerns/packages/debian_distribution_endpoints.rb
@@ -25,21 +25,23 @@ module API
namespace 'debian_distributions' do
helpers do
params :optional_distribution_params do
- optional :suite, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Suite'
- optional :origin, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Origin'
- optional :label, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Label'
- optional :version, type: String, regexp: Gitlab::Regex.debian_version_regex, desc: 'The Debian Version'
- optional :description, type: String, desc: 'The Debian Description'
- optional :valid_time_duration_seconds, type: Integer, desc: 'The duration before the Release file should be considered expired by the client'
+ optional :suite, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Suite', documentation: { example: 'unstable' }
+ optional :origin, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Origin', documentation: { example: 'Grep' }
+ optional :label, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Label', documentation: { example: 'grep.be' }
+ optional :version, type: String, regexp: Gitlab::Regex.debian_version_regex, desc: 'The Debian Version', documentation: { example: '12' }
+ optional :description, type: String, desc: 'The Debian Description', documentation: { example: 'My description' }
+ optional :valid_time_duration_seconds, type: Integer, desc: 'The duration before the Release file should be considered expired by the client', documentation: { example: 604800 }
optional :components, type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
regexp: Gitlab::Regex.debian_component_regex,
- desc: 'The list of Components'
+ desc: 'The list of Components',
+ documentation: { example: 'main' }
optional :architectures, type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
regexp: Gitlab::Regex.debian_architecture_regex,
- desc: 'The list of Architectures'
+ desc: 'The list of Architectures',
+ documentation: { example: 'amd64' }
end
end
@@ -63,11 +65,18 @@ module API
# POST {projects|groups}/:id/debian_distributions
desc 'Create a Debian Distribution' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 201, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
post '/' do
@@ -87,12 +96,18 @@ module API
# GET {projects|groups}/:id/debian_distributions
desc 'Get a list of Debian Distributions' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
use :pagination
- optional :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ optional :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
get '/' do
@@ -107,11 +122,17 @@ module API
# GET {projects|groups}/:id/debian_distributions/:codename
desc 'Get a Debian Distribution' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
end
get '/:codename' do
authorize_read_package!(project_or_group)
@@ -122,11 +143,17 @@ module API
# GET {projects|groups}/:id/debian_distributions/:codename/key
desc 'Get a Debian Distribution Key' do
detail 'This feature was introduced in 14.4'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
end
get '/:codename/key.asc' do
authorize_read_package!(project_or_group)
@@ -141,11 +168,18 @@ module API
# PUT {projects|groups}/:id/debian_distributions/:codename
desc 'Update a Debian Distribution' do
detail 'This feature was introduced in 14.0'
- success ::API::Entities::Packages::Debian::Distribution
+ success code: 200, model: ::API::Entities::Packages::Debian::Distribution
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
put '/:codename' do
@@ -165,10 +199,18 @@ module API
# DELETE {projects|groups}/:id/debian_distributions/:codename
desc 'Delete a Debian Distribution' do
detail 'This feature was introduced in 14.0'
+ success code: 202
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_distribution]
end
params do
- requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename', documentation: { example: 'unstable' }
use :optional_distribution_params
end
delete '/:codename' do
diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index 2883944a745..842250d351b 100644
--- a/lib/api/concerns/packages/debian_package_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -24,11 +24,11 @@ module API
helpers do
params :shared_package_file_params do
- requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex
- requires :letter, type: String, desc: 'The Debian Classification (first-letter or lib-first-letter)'
- requires :package_name, type: String, desc: 'The Debian Source Package Name', regexp: Gitlab::Regex.debian_package_name_regex
- requires :package_version, type: String, desc: 'The Debian Source Package Version', regexp: Gitlab::Regex.debian_version_regex
- requires :file_name, type: String, desc: 'The Debian File Name'
+ requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex, documentation: { example: 'my-distro' }
+ requires :letter, type: String, desc: 'The Debian Classification (first-letter or lib-first-letter)', documentation: { example: 'a' }
+ requires :package_name, type: String, desc: 'The Debian Source Package Name', regexp: Gitlab::Regex.debian_package_name_regex, documentation: { example: 'my-pkg' }
+ requires :package_version, type: String, desc: 'The Debian Source Package Version', regexp: Gitlab::Regex.debian_version_regex, documentation: { example: '1.0.0' }
+ requires :file_name, type: String, desc: 'The Debian File Name', documentation: { example: 'example_1.0.0~alpha2_amd64.deb' }
end
def distribution_from!(container)
@@ -79,7 +79,7 @@ module API
content_type :txt, 'text/plain'
params do
- requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex
+ requires :distribution, type: String, desc: 'The Debian Codename or Suite', regexp: Gitlab::Regex.debian_distribution_regex, documentation: { example: 'my-distro' }
end
namespace 'dists/*distribution', requirements: DISTRIBUTION_REQUIREMENTS do
@@ -87,6 +87,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Release.22_files
desc 'The Release file signature' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -98,6 +106,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Release.22_files
desc 'The unsigned Release file' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -109,6 +125,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Release.22_files
desc 'The signed Release file' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -117,12 +141,12 @@ module API
end
params do
- requires :component, type: String, desc: 'The Debian Component', regexp: Gitlab::Regex.debian_component_regex
+ requires :component, type: String, desc: 'The Debian Component', regexp: Gitlab::Regex.debian_component_regex, documentation: { example: 'main' }
end
namespace ':component', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
params do
- requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
+ requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex, documentation: { example: 'binary-amd64' }
end
namespace 'debian-installer/binary-:architecture' do
@@ -130,6 +154,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Packages.22_Indices
desc 'The installer (udeb) binary files index' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -141,6 +173,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
desc 'The installer (udeb) binary files index by hash' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -154,6 +194,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Sources.22_Indices
desc 'The source files index' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -165,6 +213,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
desc 'The source files index by hash' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -174,7 +230,7 @@ module API
end
params do
- requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
+ requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex, documentation: { example: 'binary-amd64' }
end
namespace 'binary-:architecture', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
@@ -182,6 +238,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format#A.22Packages.22_Indices
desc 'The binary files index' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -193,6 +257,14 @@ module API
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
desc 'The binary files index by hash' do
detail 'This feature was introduced in GitLab 15.4'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index 4cc680068b6..f26b3a1d8c2 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -26,13 +26,39 @@ module API
authenticate_non_get!
end
+ helpers do
+ def redirect_or_present_audit_report
+ redirect_registry_request(
+ forward_to_registry: true,
+ package_type: :npm,
+ path: options[:path][0],
+ body: Gitlab::Json.dump(request.POST),
+ target: project_or_nil,
+ method: route.request_method
+ ) do
+ authorize_read_package!(project)
+
+ status :ok
+ present []
+ end
+ end
+ end
+
params do
requires :package_name, type: String, desc: 'Package name'
end
namespace '-/package/*package_name' do
desc 'Get all tags for a given an NPM package' do
detail 'This feature was introduced in GitLab 12.7'
- success ::API::Entities::NpmPackageTag
+ success [
+ { code: 200, model: ::API::Entities::NpmPackageTag }
+ ]
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
get 'dist-tags', format: false, requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
package_name = params[:package_name]
@@ -56,6 +82,14 @@ module API
namespace 'dist-tags/:tag', requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
desc 'Create or Update the given tag for the given NPM package and version' do
detail 'This feature was introduced in GitLab 12.7'
+ success code: 204
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
put format: false do
package_name = params[:package_name]
@@ -79,6 +113,14 @@ module API
desc 'Deletes the given tag' do
detail 'This feature was introduced in GitLab 12.7'
+ success code: 204
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
delete format: false do
package_name = params[:package_name]
@@ -104,6 +146,16 @@ module API
desc 'NPM registry metadata endpoint' do
detail 'This feature was introduced in GitLab 11.8'
+ success [
+ { code: 200, model: ::API::Entities::NpmPackage, message: 'Ok' },
+ { code: 302, message: 'Found (redirect)' }
+ ]
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
params do
requires :package_name, type: String, desc: 'Package name'
@@ -130,6 +182,44 @@ module API
with: ::API::Entities::NpmPackage
end
end
+
+ desc 'NPM registry bulk advisory endpoint' do
+ detail 'This feature was introduced in GitLab 15.6'
+ success [
+ { code: 200, message: 'Ok' },
+ { code: 307, message: 'Temporary Redirect' }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[npm_packages]
+ end
+ route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
+ post '-/npm/v1/security/advisories/bulk' do
+ redirect_or_present_audit_report
+ end
+
+ desc 'NPM registry quick audit endpoint' do
+ detail 'This feature was introduced in GitLab 15.6'
+ success [
+ { code: 200, message: 'Ok' },
+ { code: 307, message: 'Temporary Redirect' }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[npm_packages]
+ end
+ route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
+ post '-/npm/v1/security/audits/quick' do
+ redirect_or_present_audit_report
+ end
end
end
end
diff --git a/lib/api/concerns/packages/nuget_endpoints.rb b/lib/api/concerns/packages/nuget_endpoints.rb
index e0328e488c6..31ecb529c3c 100644
--- a/lib/api/concerns/packages/nuget_endpoints.rb
+++ b/lib/api/concerns/packages/nuget_endpoints.rb
@@ -55,6 +55,13 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/service-index
desc 'The NuGet Service Index' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 200, model: ::API::Entities::Nuget::ServiceIndex
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get 'index', format: :json, urgency: :default do
authorize_read_package!(project_or_group)
@@ -67,7 +74,7 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'MyNuGetPkg' }
end
namespace '/metadata/*package_name' do
after_validation do
@@ -76,6 +83,13 @@ module API
desc 'The NuGet Metadata Service - Package name level' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::PackagesMetadata
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get 'index', format: :json, urgency: :low do
present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages(params[:package_name])),
@@ -84,9 +98,16 @@ module API
desc 'The NuGet Metadata Service - Package name and version level' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::PackageMetadata
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: '1.0.0' }
end
get '*package_version', format: :json, urgency: :low do
present ::Packages::Nuget::PackageMetadataPresenter.new(find_package(params[:package_name], params[:package_version])),
@@ -96,9 +117,9 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
params do
- optional :q, type: String, desc: 'The search term'
- optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX
- optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX
+ optional :q, type: String, desc: 'The search term', documentation: { example: 'MyNuGet' }
+ optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX, documentation: { example: 1 }
+ optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX, documentation: { example: 1 }
optional :prerelease, type: ::Grape::API::Boolean, desc: 'Include prerelease versions', default: true
end
namespace '/query' do
@@ -108,6 +129,13 @@ module API
desc 'The NuGet Search Service' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::SearchResults
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get format: :json, urgency: :low do
search_options = {
diff --git a/lib/api/container_registry_event.rb b/lib/api/container_registry_event.rb
index 9acf2fca1b3..9e59401ddf6 100644
--- a/lib/api/container_registry_event.rb
+++ b/lib/api/container_registry_event.rb
@@ -26,15 +26,21 @@ module API
desc 'Receives notifications from the container registry when an operation occurs' do
detail 'This feature was introduced in GitLab 12.10'
consumes [:json, DOCKER_DISTRIBUTION_EVENTS_V1_JSON]
+ success code: 200, message: 'Success'
+ failure [
+ { code: 401, message: 'Invalid Token' }
+ ]
+ tags %w[container_registry_event]
end
params do
requires :events, type: Array, desc: 'Event notifications' do
requires :action, type: String, desc: 'The action to perform, `push`, `delete`',
values: %w[push delete].freeze
optional :target, type: Hash, desc: 'The target of the action' do
- optional :tag, type: String, desc: 'The target tag'
- optional :repository, type: String, desc: 'The target repository'
- optional :digest, type: String, desc: 'Unique identifier for target image manifest'
+ optional :tag, type: String, desc: 'The target tag', documentation: { example: 'latest' }
+ optional :repository, type: String, desc: 'The target repository', documentation: { example: 'group/p1' }
+ optional :digest, type: String, desc: 'Unique identifier for target image manifest',
+ documentation: { example: 'imagedigest' }
end
end
end
diff --git a/lib/api/container_repositories.rb b/lib/api/container_repositories.rb
index f2dd1fa21fd..b6b5fe10332 100644
--- a/lib/api/container_repositories.rb
+++ b/lib/api/container_repositories.rb
@@ -14,12 +14,17 @@ module API
namespace 'registry' do
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID of the repository'
end
resource :repositories, requirements: { id: /[0-9]*/ } do
desc 'Get a container repository' do
detail 'This feature was introduced in GitLab 13.6.'
success Entities::ContainerRegistry::Repository
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Repository Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included'
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 0962d749558..105a0955912 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -30,7 +30,7 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The group ID or full group path.'
end
namespace ':id/-/packages/debian' do
@@ -42,8 +42,15 @@ module API
use :shared_package_file_params
end
- desc 'The package' do
+ desc 'Download Debian package' do
detail 'This feature was introduced in GitLab 14.2'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
index df3b6e774ae..23a542e4183 100644
--- a/lib/api/debian_project_packages.rb
+++ b/lib/api/debian_project_packages.rb
@@ -45,8 +45,15 @@ module API
use :shared_package_file_params
end
- desc 'The package' do
+ desc 'Download Debian package' do
detail 'This feature was introduced in GitLab 14.2'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
end
route_setting :authentication, authenticate_non_public: true
@@ -55,13 +62,25 @@ module API
end
params do
- requires :file_name, type: String, desc: 'The file name'
+ requires :file_name, type: String, desc: 'The file name', documentation: { example: 'example_1.0.0~alpha2_amd64.deb' }
end
namespace ':file_name', requirements: FILE_NAME_REQUIREMENTS do
format :txt
content_type :json, Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
+ desc 'Upload Debian package' do
+ detail 'This feature was introduced in GitLab 14.0'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
+ end
+
# PUT {projects|groups}/:id/packages/debian/:file_name
params do
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
@@ -91,6 +110,16 @@ module API
end
# PUT {projects|groups}/:id/packages/debian/:file_name/authorize
+ desc 'Authorize Debian package upload' do
+ detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[debian_packages]
+ end
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
put 'authorize' do
authorize_workhorse!(
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index 141f089b5e1..3a0eea677b8 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -50,6 +50,14 @@ module API
type: DateTime,
desc: 'Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)'
+ optional :finished_after,
+ type: DateTime,
+ desc: 'Return deployments finished after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)'
+
+ optional :finished_before,
+ type: DateTime,
+ desc: 'Return deployments finished before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)'
+
optional :environment,
type: String,
desc: 'The name of the environment to filter deployments by'
@@ -64,7 +72,7 @@ module API
authorize! :read_deployment, user_project
deployments =
- DeploymentsFinder.new(params.merge(project: user_project))
+ DeploymentsFinder.new(declared_params(include_missing: false).merge(project: user_project))
.execute.with_api_entity_associations
present paginate(deployments), with: Entities::Deployment
diff --git a/lib/api/entities/appearance.rb b/lib/api/entities/appearance.rb
index a09faf55f48..94a39568393 100644
--- a/lib/api/entities/appearance.rb
+++ b/lib/api/entities/appearance.rb
@@ -4,6 +4,7 @@ module API
module Entities
class Appearance < Grape::Entity
expose :title
+ expose :short_title
expose :description
expose :logo do |appearance, options|
diff --git a/lib/api/entities/basic_success.rb b/lib/api/entities/basic_success.rb
new file mode 100644
index 00000000000..37388f56221
--- /dev/null
+++ b/lib/api/entities/basic_success.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ # Simple representation for endpoints that returns a trivial success response.
+ class BasicSuccess < Grape::Entity
+ expose :success, documentation: { type: 'boolean' } do
+ true
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/batched_background_migration.rb b/lib/api/entities/batched_background_migration.rb
index eba17ff98f4..08e4681e0aa 100644
--- a/lib/api/entities/batched_background_migration.rb
+++ b/lib/api/entities/batched_background_migration.rb
@@ -3,12 +3,12 @@
module API
module Entities
class BatchedBackgroundMigration < Grape::Entity
- expose :id
- expose :job_class_name
- expose :table_name
- expose :status, &:status_name
- expose :progress
- expose :created_at
+ expose :id, documentation: { type: :string, example: "1234" }
+ expose :job_class_name, documentation: { type: :string, example: "CopyColumnUsingBackgroundMigrationJob" }
+ expose :table_name, documentation: { type: :string, example: "events" }
+ expose :status_name, as: :status, override: true, documentation: { type: :string, example: "active" }
+ expose :progress, documentation: { type: :float, example: 50 }
+ expose :created_at, documentation: { type: :dateTime, example: "2022-11-28T16:26:39+02:00" }
end
end
end
diff --git a/lib/api/entities/ci/job_request/hook.rb b/lib/api/entities/ci/job_request/hook.rb
new file mode 100644
index 00000000000..2d155bb1c45
--- /dev/null
+++ b/lib/api/entities/ci/job_request/hook.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ci
+ module JobRequest
+ class Hook < Grape::Entity
+ expose :name, :script
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_request/response.rb b/lib/api/entities/ci/job_request/response.rb
index 9de415ebacb..cfdbeed79b6 100644
--- a/lib/api/entities/ci/job_request/response.rb
+++ b/lib/api/entities/ci/job_request/response.rb
@@ -23,6 +23,9 @@ module API
expose :runner_variables, as: :variables
expose :steps, using: Entities::Ci::JobRequest::Step
+ expose :runtime_hooks, as: :hooks,
+ using: Entities::Ci::JobRequest::Hook,
+ if: ->(job) { ::Feature.enabled?(:ci_hooks_pre_get_sources_script, job.project) }
expose :image, using: Entities::Ci::JobRequest::Image
expose :services, using: Entities::Ci::JobRequest::Service
expose :artifacts, using: Entities::Ci::JobRequest::Artifacts
diff --git a/lib/api/entities/ci/runner_details.rb b/lib/api/entities/ci/runner_details.rb
index 9b1decca274..8aa134dc669 100644
--- a/lib/api/entities/ci/runner_details.rb
+++ b/lib/api/entities/ci/runner_details.rb
@@ -14,7 +14,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
expose :projects, with: Entities::BasicProjectDetails do |runner, options|
- if options[:current_user].admin? # rubocop: disable Cop/UserAdmin
+ if options[:current_user].can_read_all_resources?
runner.projects
else
options[:current_user].authorized_projects.where(id: runner.runner_projects.pluck(:project_id))
@@ -23,7 +23,7 @@ module API
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
expose :groups, with: Entities::BasicGroupDetails do |runner, options|
- if options[:current_user].admin? # rubocop: disable Cop/UserAdmin
+ if options[:current_user].can_read_all_resources?
runner.groups
else
options[:current_user].authorized_groups.where(id: runner.runner_namespaces.pluck(:namespace_id))
diff --git a/lib/api/entities/ci/secure_file.rb b/lib/api/entities/ci/secure_file.rb
index d957e4488fd..a234ada6f82 100644
--- a/lib/api/entities/ci/secure_file.rb
+++ b/lib/api/entities/ci/secure_file.rb
@@ -4,13 +4,14 @@ module API
module Entities
module Ci
class SecureFile < Grape::Entity
- expose :id
- expose :name
- expose :checksum
- expose :checksum_algorithm
- expose :created_at
- expose :expires_at
- expose :metadata
+ expose :id, documentation: { type: 'integer', example: 123 }
+ expose :name, documentation: { type: 'string', example: 'upload-keystore.jks' }
+ expose :checksum,
+documentation: { type: 'string', example: '16630b189ab34b2e3504f4758e1054d2e478deda510b2b08cc0ef38d12e80aac' }
+ expose :checksum_algorithm, documentation: { type: 'string', example: 'sha256' }
+ expose :created_at, documentation: { type: 'dateTime', example: '2022-02-22T22:22:22.222Z' }
+ expose :expires_at, documentation: { type: 'dateTime', example: '2022-09-21T14:56:00.000Z' }
+ expose :metadata, documentation: { type: 'Hash', example: { "id" => "75949910542696343243264405377658443914" } }
end
end
end
diff --git a/lib/api/entities/commit_signature.rb b/lib/api/entities/commit_signature.rb
index 9430dd5e2a2..9c30c3c59ea 100644
--- a/lib/api/entities/commit_signature.rb
+++ b/lib/api/entities/commit_signature.rb
@@ -10,6 +10,8 @@ module API
::API::Entities::GpgCommitSignature.represent commit_signature(commit), options
elsif commit.signature.is_a?(::CommitSignatures::X509CommitSignature)
::API::Entities::X509Signature.represent commit.signature, options
+ elsif commit.signature.is_a?(::CommitSignatures::SshSignature)
+ ::API::Entities::SshSignature.represent(commit.signature, options)
end
end
diff --git a/lib/api/entities/conan_package/conan_package_manifest.rb b/lib/api/entities/conan_package/conan_package_manifest.rb
index e6acfe1912f..70ab498c56a 100644
--- a/lib/api/entities/conan_package/conan_package_manifest.rb
+++ b/lib/api/entities/conan_package/conan_package_manifest.rb
@@ -4,7 +4,7 @@ module API
module Entities
module ConanPackage
class ConanPackageManifest < Grape::Entity
- expose :package_urls, merge: true
+ expose :package_urls, merge: true, documentation: { type: 'object', example: '{ "conan_package.tgz": "https://gitlab.example.com/api/v4/packages/conan/v1/files/my-package/1.0/my-group+my-project/stable/packages/103f6067a947f366ef91fc1b7da351c588d1827f/0/conan_package.tgz"' }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_package_snapshot.rb b/lib/api/entities/conan_package/conan_package_snapshot.rb
index d7fdda09b5a..5cf623c53df 100644
--- a/lib/api/entities/conan_package/conan_package_snapshot.rb
+++ b/lib/api/entities/conan_package/conan_package_snapshot.rb
@@ -4,7 +4,11 @@ module API
module Entities
module ConanPackage
class ConanPackageSnapshot < Grape::Entity
- expose :package_snapshot, merge: true
+ expose :package_snapshot, merge: true,
+ documentation: {
+ type: 'object',
+ example: '{ "conan_package.tgz": "749b29bdf72587081ca03ec033ee59dc" }'
+ }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_recipe_manifest.rb b/lib/api/entities/conan_package/conan_recipe_manifest.rb
index ecaa142cef9..0b29f0c5058 100644
--- a/lib/api/entities/conan_package/conan_recipe_manifest.rb
+++ b/lib/api/entities/conan_package/conan_recipe_manifest.rb
@@ -4,7 +4,7 @@ module API
module Entities
module ConanPackage
class ConanRecipeManifest < Grape::Entity
- expose :recipe_urls, merge: true
+ expose :recipe_urls, merge: true, documentation: { type: 'object', example: '{ "conan_sources.tgz": "https://gitlab.example.com/api/v4/packages/conan/v1/files/my-package/1.0/my-group+my-project/stable/0/export/conan_sources.tgz" }' }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_recipe_snapshot.rb b/lib/api/entities/conan_package/conan_recipe_snapshot.rb
index 09a60d23727..f9806e90816 100644
--- a/lib/api/entities/conan_package/conan_recipe_snapshot.rb
+++ b/lib/api/entities/conan_package/conan_recipe_snapshot.rb
@@ -4,7 +4,11 @@ module API
module Entities
module ConanPackage
class ConanRecipeSnapshot < Grape::Entity
- expose :recipe_snapshot, merge: true
+ expose :recipe_snapshot, merge: true,
+ documentation: {
+ type: 'object',
+ example: '{ "conan_sources.tgz": "eadf19b33f4c3c7e113faabf26e76277" }'
+ }
end
end
end
diff --git a/lib/api/entities/conan_package/conan_upload_urls.rb b/lib/api/entities/conan_package/conan_upload_urls.rb
index c14963c87f5..fd5ea80068c 100644
--- a/lib/api/entities/conan_package/conan_upload_urls.rb
+++ b/lib/api/entities/conan_package/conan_upload_urls.rb
@@ -4,7 +4,7 @@ module API
module Entities
module ConanPackage
class ConanUploadUrls < Grape::Entity
- expose :upload_urls, merge: true
+ expose :upload_urls, merge: true, documentation: { type: 'object', example: '{ "conan_package.tgz": "https://gitlab.example.com/api/v4/packages/conan/v1/files/my-package/1.0/my-group+my-project/stable/0/package/103f6067a947f366ef91fc1b7da351c588d1827f/0/conan_package.tgz" }' }
end
end
end
diff --git a/lib/api/entities/container_registry.rb b/lib/api/entities/container_registry.rb
index d12c8142e69..cadd45cb0eb 100644
--- a/lib/api/entities/container_registry.rb
+++ b/lib/api/entities/container_registry.rb
@@ -4,9 +4,9 @@ module API
module Entities
module ContainerRegistry
class Tag < Grape::Entity
- expose :name
- expose :path
- expose :location
+ expose :name, documentation: { type: 'string', example: 'latest' }
+ expose :path, documentation: { type: 'string', example: 'namespace1/project1/test_image_1:latest' }
+ expose :location, documentation: { type: 'string', example: 'registry.dev/namespace1/project1/test_image_1:latest' }
end
class Repository < Grape::Entity
@@ -19,10 +19,11 @@ module API
expose :location, documentation: { type: 'string', example: 'gitlab.example.com/group/project/releases' }
expose :created_at, documentation: { type: 'dateTime', example: '2019-01-10T13:39:08.229Z' }
expose :expiration_policy_started_at, as: :cleanup_policy_started_at, documentation: { type: 'dateTime', example: '2020-08-17T03:12:35.489Z' }
- expose :tags_count, if: -> (_, options) { options[:tags_count] }
+ expose :tags_count, if: -> (_, options) { options[:tags_count] }, documentation: { type: 'integer', example: 3 }
expose :tags, using: Tag, if: -> (_, options) { options[:tags] }
- expose :delete_api_path, if: ->(object, options) { Ability.allowed?(options[:user], :admin_container_image, object) }
- expose :size, if: -> (_, options) { options[:size] }
+ expose :delete_api_path, if: ->(object, options) { Ability.allowed?(options[:user], :admin_container_image, object) },
+ documentation: { type: 'string', example: 'delete/api/path' }
+ expose :size, if: -> (_, options) { options[:size] }, documentation: { type: 'integer', example: 12345 }
private
@@ -32,11 +33,11 @@ module API
end
class TagDetails < Tag
- expose :revision
- expose :short_revision
- expose :digest
- expose :created_at
- expose :total_size
+ expose :revision, documentation: { type: 'string', example: 'tagrevision' }
+ expose :short_revision, documentation: { type: 'string', example: 'shortrevison' }
+ expose :digest, documentation: { type: 'string', example: 'shadigest' }
+ expose :created_at, documentation: { type: 'dateTime', example: '2022-01-10T13:39:08.229Z' }
+ expose :total_size, documentation: { type: 'integer', example: 3 }
end
end
end
diff --git a/lib/api/entities/event.rb b/lib/api/entities/event.rb
index f750d728e03..e81e89a8393 100644
--- a/lib/api/entities/event.rb
+++ b/lib/api/entities/event.rb
@@ -3,11 +3,15 @@
module API
module Entities
class Event < Grape::Entity
- expose :id
- expose :project_id, :action_name
- expose :target_id, :target_iid, :target_type, :author_id
- expose :target_title
- expose :created_at
+ expose :id, documentation: { type: 'integer', example: 1 }
+ expose :project_id, documentation: { type: 'integer', example: 2 }
+ expose :action_name, documentation: { type: 'string', example: 'closed' }
+ expose :target_id, documentation: { type: 'integer', example: 160 }
+ expose :target_iid, documentation: { type: 'integer', example: 157 }
+ expose :target_type, documentation: { type: 'string', example: 'Issue' }
+ expose :author_id, documentation: { type: 'integer', example: 25 }
+ expose :target_title, documentation: { type: 'string', example: 'Public project search field' }
+ expose :created_at, documentation: { type: 'string', example: '2017-02-09T10:43:19.667Z' }
expose :note, using: Entities::Note, if: ->(event, options) { event.note? }
expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author }
expose :wiki_page, using: Entities::WikiPageBasic, if: ->(event, _options) { event.wiki_page? }
@@ -17,7 +21,7 @@ module API
using: Entities::PushEventPayload,
if: -> (event, _) { event.push_action? }
- expose :author_username do |event, options|
+ expose :author_username, documentation: { type: 'string', example: 'root' } do |event, options|
event.author&.username
end
end
diff --git a/lib/api/entities/issuable_references.rb b/lib/api/entities/issuable_references.rb
index 1bf078847cf..7b966b85800 100644
--- a/lib/api/entities/issuable_references.rb
+++ b/lib/api/entities/issuable_references.rb
@@ -3,15 +3,15 @@
module API
module Entities
class IssuableReferences < Grape::Entity
- expose :short do |issuable|
+ expose :short, documentation: { type: "string", example: "&6" } do |issuable|
issuable.to_reference
end
- expose :relative do |issuable, options|
+ expose :relative, documentation: { type: "string", example: "&6" } do |issuable, options|
issuable.to_reference(options[:group] || options[:project])
end
- expose :full do |issuable|
+ expose :full, documentation: { type: "string", example: "test&6" } do |issuable|
issuable.to_reference(full: true)
end
end
diff --git a/lib/api/entities/issuable_time_stats.rb b/lib/api/entities/issuable_time_stats.rb
index f93b4651b1f..717d2282441 100644
--- a/lib/api/entities/issuable_time_stats.rb
+++ b/lib/api/entities/issuable_time_stats.rb
@@ -7,12 +7,12 @@ module API
Gitlab::TimeTrackingFormatter.output(time_spent)
end
- expose :time_estimate
- expose :total_time_spent
- expose :human_time_estimate
+ expose :time_estimate, documentation: { type: 'integer', example: 12600 }
+ expose :total_time_spent, documentation: { type: 'integer', example: 3600 }
+ expose :human_time_estimate, documentation: { type: 'string', example: '3h 30m' }
with_options(format_with: :time_tracking_formatter) do
- expose :total_time_spent, as: :human_total_time_spent
+ expose :total_time_spent, as: :human_total_time_spent, documentation: { type: 'string', example: '1h' }
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/lib/api/entities/metric_image.rb b/lib/api/entities/metric_image.rb
index fd5e3a62e40..3e4566832c9 100644
--- a/lib/api/entities/metric_image.rb
+++ b/lib/api/entities/metric_image.rb
@@ -3,7 +3,13 @@
module API
module Entities
class MetricImage < Grape::Entity
- expose :id, :created_at, :filename, :file_path, :url, :url_text
+ expose :id, documentation: { type: 'integer', example: 23 }
+ expose :created_at, documentation: { type: 'dateTime', example: '2020-11-13T00:06:18.084Z' }
+ expose :filename, documentation: { type: 'string', example: 'file.png' }
+ expose :file_path, documentation: { type: 'string',
+ example: '/uploads/-/system/alert_metric_image/file/23/file.png' }
+ expose :url, documentation: { type: 'string', example: 'https://example.com/metric' }
+ expose :url_text, documentation: { type: 'string', example: 'An example metric' }
end
end
end
diff --git a/lib/api/entities/milestone.rb b/lib/api/entities/milestone.rb
index b191210a234..ea73ade46cd 100644
--- a/lib/api/entities/milestone.rb
+++ b/lib/api/entities/milestone.rb
@@ -10,7 +10,7 @@ module API
expose :state, :created_at, :updated_at
expose :due_date
expose :start_date
- expose :expired?, as: :expired
+ expose :expired
expose :web_url do |milestone, _options|
Gitlab::UrlBuilder.build(milestone)
diff --git a/lib/api/entities/ml/mlflow/experiment.rb b/lib/api/entities/ml/mlflow/experiment.rb
index 54e0fe63985..51650c36d98 100644
--- a/lib/api/entities/ml/mlflow/experiment.rb
+++ b/lib/api/entities/ml/mlflow/experiment.rb
@@ -9,6 +9,7 @@ module API
expose :name
expose(:lifecycle_stage) { |experiment| experiment.deleted_on? ? 'deleted' : 'active' }
expose(:artifact_location) { |experiment| 'not_implemented' }
+ expose :metadata, as: :tags, using: KeyValue
end
end
end
diff --git a/lib/api/entities/ml/mlflow/run_param.rb b/lib/api/entities/ml/mlflow/key_value.rb
index 75fee738f8b..cf2c32f6f44 100644
--- a/lib/api/entities/ml/mlflow/run_param.rb
+++ b/lib/api/entities/ml/mlflow/key_value.rb
@@ -4,7 +4,7 @@ module API
module Entities
module Ml
module Mlflow
- class RunParam < Grape::Entity
+ class KeyValue < Grape::Entity
expose :name, as: :key
expose :value
end
diff --git a/lib/api/entities/ml/mlflow/run.rb b/lib/api/entities/ml/mlflow/run.rb
index 8b16c67611f..01d85e8862b 100644
--- a/lib/api/entities/ml/mlflow/run.rb
+++ b/lib/api/entities/ml/mlflow/run.rb
@@ -9,7 +9,8 @@ module API
expose :itself, using: RunInfo, as: :info
expose :data do
expose :metrics, using: Metric
- expose :params, using: RunParam
+ expose :params, using: KeyValue
+ expose :metadata, as: :tags, using: KeyValue
end
end
end
diff --git a/lib/api/entities/namespace.rb b/lib/api/entities/namespace.rb
index f11303d41a6..15bc7d158c4 100644
--- a/lib/api/entities/namespace.rb
+++ b/lib/api/entities/namespace.rb
@@ -3,7 +3,7 @@
module API
module Entities
class Namespace < Entities::NamespaceBasic
- expose :members_count_with_descendants, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _|
+ expose :members_count_with_descendants, documentation: { type: 'integer', example: 5 }, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _|
namespace.users_with_descendants.count
end
diff --git a/lib/api/entities/namespace_basic.rb b/lib/api/entities/namespace_basic.rb
index 2b9dd0b5f4d..4264326cdc2 100644
--- a/lib/api/entities/namespace_basic.rb
+++ b/lib/api/entities/namespace_basic.rb
@@ -3,9 +3,15 @@
module API
module Entities
class NamespaceBasic < Grape::Entity
- expose :id, :name, :path, :kind, :full_path, :parent_id, :avatar_url
+ expose :id, documentation: { type: 'integer', example: 2 }
+ expose :name, documentation: { type: 'string', example: 'project' }
+ expose :path, documentation: { type: 'string', example: 'my_project' }
+ expose :kind, documentation: { type: 'string', example: 'project' }
+ expose :full_path, documentation: { type: 'string', example: 'group/my_project' }
+ expose :parent_id, documentation: { type: 'integer', example: 1 }
+ expose :avatar_url, documentation: { type: 'string', example: 'https://example.com/avatar/12345' }
- expose :web_url do |namespace|
+ expose :web_url, documentation: { type: 'string', example: 'https://example.com/group/my_project' } do |namespace|
if namespace.user_namespace?
Gitlab::Routing.url_helpers.user_url(namespace.owner)
else
diff --git a/lib/api/entities/namespace_existence.rb b/lib/api/entities/namespace_existence.rb
index d93078ecdac..ac9511930ab 100644
--- a/lib/api/entities/namespace_existence.rb
+++ b/lib/api/entities/namespace_existence.rb
@@ -3,7 +3,8 @@
module API
module Entities
class NamespaceExistence < Grape::Entity
- expose :exists, :suggests
+ expose :exists, documentation: { type: 'boolean' }
+ expose :suggests, documentation: { type: 'string', is_array: true, example: 'my-group1' }
end
end
end
diff --git a/lib/api/entities/npm_package.rb b/lib/api/entities/npm_package.rb
index b094f3acdb6..ad864f86fd5 100644
--- a/lib/api/entities/npm_package.rb
+++ b/lib/api/entities/npm_package.rb
@@ -3,9 +3,19 @@
module API
module Entities
class NpmPackage < Grape::Entity
- expose :name
- expose :versions
- expose :dist_tags, as: 'dist-tags'
+ expose :name, documentation: { type: 'string', example: 'my_package' }
+ expose :versions,
+ documentation: {
+ type: 'object',
+ example: '{
+ "1.0.0": {
+ "name": "my_package",
+ "version": "1.0.0",
+ "dist": { "shasum": "12345", "tarball": "https://..." }
+ }
+ }'
+ }
+ expose :dist_tags, as: 'dist-tags', documentation: { type: 'object', example: '{ "latest":"1.0.1" }' }
end
end
end
diff --git a/lib/api/entities/npm_package_tag.rb b/lib/api/entities/npm_package_tag.rb
index 7f458fa037f..0a20d18e917 100644
--- a/lib/api/entities/npm_package_tag.rb
+++ b/lib/api/entities/npm_package_tag.rb
@@ -3,7 +3,7 @@
module API
module Entities
class NpmPackageTag < Grape::Entity
- expose :dist_tags, merge: true
+ expose :dist_tags, merge: true, documentation: { type: 'object', example: '{ "latest":"1.0.1" }' }
end
end
end
diff --git a/lib/api/entities/nuget/dependency.rb b/lib/api/entities/nuget/dependency.rb
index b61c37f5882..adb11376cfa 100644
--- a/lib/api/entities/nuget/dependency.rb
+++ b/lib/api/entities/nuget/dependency.rb
@@ -4,10 +4,10 @@ module API
module Entities
module Nuget
class Dependency < Grape::Entity
- expose :id, as: :@id
- expose :type, as: :@type
- expose :name, as: :id
- expose :range
+ expose :id, as: :@id, documentation: { type: 'string', example: 'http://gitlab.com/Sandbox.App/1.0.0.json#dependency' }
+ expose :type, as: :@type, documentation: { type: 'string', example: 'PackageDependency' }
+ expose :name, as: :id, documentation: { type: 'string', example: 'Dependency' }
+ expose :range, documentation: { type: 'string', example: '2.0.0' }
end
end
end
diff --git a/lib/api/entities/nuget/dependency_group.rb b/lib/api/entities/nuget/dependency_group.rb
index dcab9359fcf..8d943050cd8 100644
--- a/lib/api/entities/nuget/dependency_group.rb
+++ b/lib/api/entities/nuget/dependency_group.rb
@@ -4,10 +4,12 @@ module API
module Entities
module Nuget
class DependencyGroup < Grape::Entity
- expose :id, as: :@id
- expose :type, as: :@type
- expose :target_framework, as: :targetFramework, expose_nil: false
- expose :dependencies, using: ::API::Entities::Nuget::Dependency
+ expose :id, as: :@id, documentation: { type: 'string', example: 'http://gitlab.com/Sandbox.App/1.0.0.json#dependencygroup' }
+ expose :type, as: :@type, documentation: { type: 'string', example: 'PackageDependencyGroup' }
+ expose :target_framework, as: :targetFramework, expose_nil: false,
+ documentation: { type: 'string', example: 'fwk test' }
+ expose :dependencies, using: ::API::Entities::Nuget::Dependency,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::Dependency' }
end
end
end
diff --git a/lib/api/entities/nuget/metadatum.rb b/lib/api/entities/nuget/metadatum.rb
index 87caef41a85..256b916cb64 100644
--- a/lib/api/entities/nuget/metadatum.rb
+++ b/lib/api/entities/nuget/metadatum.rb
@@ -4,9 +4,9 @@ module API
module Entities
module Nuget
class Metadatum < Grape::Entity
- expose :project_url, as: :projectUrl, expose_nil: false
- expose :license_url, as: :licenseUrl, expose_nil: false
- expose :icon_url, as: :iconUrl, expose_nil: false
+ expose :project_url, as: :projectUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/project' }
+ expose :license_url, as: :licenseUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/license' }
+ expose :icon_url, as: :iconUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/icon' }
end
end
end
diff --git a/lib/api/entities/nuget/package_metadata.rb b/lib/api/entities/nuget/package_metadata.rb
index e1c2a1ae161..1c94426bdd6 100644
--- a/lib/api/entities/nuget/package_metadata.rb
+++ b/lib/api/entities/nuget/package_metadata.rb
@@ -4,9 +4,10 @@ module API
module Entities
module Nuget
class PackageMetadata < Grape::Entity
- expose :json_url, as: :@id
- expose :archive_url, as: :packageContent
- expose :catalog_entry, as: :catalogEntry, using: ::API::Entities::Nuget::PackageMetadataCatalogEntry
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :archive_url, as: :packageContent, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/download/MyNuGetPkg/1.3.0.17/helloworld.1.3.0.17.nupkg' }
+ expose :catalog_entry, as: :catalogEntry, using: ::API::Entities::Nuget::PackageMetadataCatalogEntry,
+ documentation: { type: 'API::Entities::Nuget::PackageMetadataCatalogEntry' }
end
end
end
diff --git a/lib/api/entities/nuget/package_metadata_catalog_entry.rb b/lib/api/entities/nuget/package_metadata_catalog_entry.rb
index 5533f857596..ce328c5a5ca 100644
--- a/lib/api/entities/nuget/package_metadata_catalog_entry.rb
+++ b/lib/api/entities/nuget/package_metadata_catalog_entry.rb
@@ -4,15 +4,17 @@ module API
module Entities
module Nuget
class PackageMetadataCatalogEntry < Grape::Entity
- expose :json_url, as: :@id
- expose :authors
- expose :dependency_groups, as: :dependencyGroups, using: ::API::Entities::Nuget::DependencyGroup
- expose :package_name, as: :id
- expose :package_version, as: :version
- expose :tags
- expose :archive_url, as: :packageContent
- expose :summary
- expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :authors, documentation: { type: 'string', example: 'Author' }
+ expose :dependency_groups, as: :dependencyGroups, using: ::API::Entities::Nuget::DependencyGroup,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::DependencyGroup' }
+ expose :package_name, as: :id, documentation: { type: 'string', example: 'MyNuGetPkg' }
+ expose :package_version, as: :version, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :tags, documentation: { type: 'string', example: 'tag#1 tag#2' }
+ expose :archive_url, as: :packageContent, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/download/MyNuGetPkg/1.3.0.17/helloworld.1.3.0.17.nupkg' }
+ expose :summary, documentation: { type: 'string', example: 'Summary' }
+ expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true,
+ documentation: { type: 'API::Entities::Nuget::Metadatum' }
end
end
end
diff --git a/lib/api/entities/nuget/packages_metadata.rb b/lib/api/entities/nuget/packages_metadata.rb
index 1cdf2491725..e556df0ce1f 100644
--- a/lib/api/entities/nuget/packages_metadata.rb
+++ b/lib/api/entities/nuget/packages_metadata.rb
@@ -4,8 +4,9 @@ module API
module Entities
module Nuget
class PackagesMetadata < Grape::Entity
- expose :count
- expose :items, using: ::API::Entities::Nuget::PackagesMetadataItem
+ expose :count, documentation: { type: 'integer', example: 1 }
+ expose :items, using: ::API::Entities::Nuget::PackagesMetadataItem,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::PackagesMetadataItem' }
end
end
end
diff --git a/lib/api/entities/nuget/packages_metadata_item.rb b/lib/api/entities/nuget/packages_metadata_item.rb
index 84cc79166f3..420a4c3941c 100644
--- a/lib/api/entities/nuget/packages_metadata_item.rb
+++ b/lib/api/entities/nuget/packages_metadata_item.rb
@@ -4,11 +4,12 @@ module API
module Entities
module Nuget
class PackagesMetadataItem < Grape::Entity
- expose :json_url, as: :@id
- expose :lower_version, as: :lower
- expose :upper_version, as: :upper
- expose :packages_count, as: :count
- expose :packages, as: :items, using: ::API::Entities::Nuget::PackageMetadata
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :lower_version, as: :lower, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :upper_version, as: :upper, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :packages_count, as: :count, documentation: { type: 'integer', example: 1 }
+ expose :packages, as: :items, using: ::API::Entities::Nuget::PackageMetadata,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::PackageMetadata' }
end
end
end
diff --git a/lib/api/entities/nuget/packages_versions.rb b/lib/api/entities/nuget/packages_versions.rb
index 498c6970d5c..e0330300ca7 100644
--- a/lib/api/entities/nuget/packages_versions.rb
+++ b/lib/api/entities/nuget/packages_versions.rb
@@ -4,7 +4,7 @@ module API
module Entities
module Nuget
class PackagesVersions < Grape::Entity
- expose :versions
+ expose :versions, documentation: { type: 'string', is_array: true, example: '1.3.0.17' }
end
end
end
diff --git a/lib/api/entities/nuget/search_result.rb b/lib/api/entities/nuget/search_result.rb
index 8e028cbad95..bb3698de30b 100644
--- a/lib/api/entities/nuget/search_result.rb
+++ b/lib/api/entities/nuget/search_result.rb
@@ -4,17 +4,18 @@ module API
module Entities
module Nuget
class SearchResult < Grape::Entity
- expose :type, as: :@type
- expose :authors
- expose :name, as: :id
- expose :name, as: :title
- expose :summary
- expose :total_downloads, as: :totalDownloads
- expose :verified
- expose :version
+ expose :type, as: :@type, documentation: { type: 'string', example: 'Package' }
+ expose :authors, documentation: { type: 'string', example: 'Author' }
+ expose :name, as: :id, documentation: { type: 'string', example: 'MyNuGetPkg' }
+ expose :name, as: :title, documentation: { type: 'string', example: 'MyNuGetPkg' }
+ expose :summary, documentation: { type: 'string', example: 'Summary' }
+ expose :total_downloads, as: :totalDownloads, documentation: { type: 'integer', example: 1 }
+ expose :verified, documentation: { type: 'boolean' }
+ expose :version, documentation: { type: 'string', example: '1.3.0.17' }
expose :versions, using: ::API::Entities::Nuget::SearchResultVersion
- expose :tags
- expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true
+ expose :tags, documentation: { type: 'string', example: 'tag#1 tag#2' }
+ expose :metadatum, using: ::API::Entities::Nuget::Metadatum, merge: true,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::Metadatum' }
end
end
end
diff --git a/lib/api/entities/nuget/search_result_version.rb b/lib/api/entities/nuget/search_result_version.rb
index 9032c964c44..fb8d8b75f83 100644
--- a/lib/api/entities/nuget/search_result_version.rb
+++ b/lib/api/entities/nuget/search_result_version.rb
@@ -4,9 +4,9 @@ module API
module Entities
module Nuget
class SearchResultVersion < Grape::Entity
- expose :json_url, as: :@id
- expose :version
- expose :downloads
+ expose :json_url, as: :@id, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/1/packages/nuget/metadata/MyNuGetPkg/1.3.0.17.json' }
+ expose :version, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :downloads, documentation: { type: 'integer', example: 1 }
end
end
end
diff --git a/lib/api/entities/nuget/search_results.rb b/lib/api/entities/nuget/search_results.rb
index 22a77dc7b6c..117904a1aff 100644
--- a/lib/api/entities/nuget/search_results.rb
+++ b/lib/api/entities/nuget/search_results.rb
@@ -4,8 +4,9 @@ module API
module Entities
module Nuget
class SearchResults < Grape::Entity
- expose :total_count, as: :totalHits
- expose :data, using: ::API::Entities::Nuget::SearchResult
+ expose :total_count, as: :totalHits, documentation: { type: 'integer', example: 1 }
+ expose :data, using: ::API::Entities::Nuget::SearchResult,
+ documentation: { is_array: true, type: 'API::Entities::Nuget::SearchResult' }
end
end
end
diff --git a/lib/api/entities/nuget/service_index.rb b/lib/api/entities/nuget/service_index.rb
index e57bd04adb9..4ab6c5ddc8b 100644
--- a/lib/api/entities/nuget/service_index.rb
+++ b/lib/api/entities/nuget/service_index.rb
@@ -4,8 +4,8 @@ module API
module Entities
module Nuget
class ServiceIndex < Grape::Entity
- expose :version
- expose :resources
+ expose :version, documentation: { type: 'string', example: '1.3.0.17' }
+ expose :resources, documentation: { type: 'object', is_array: true, example: '{ "@id": "https://gitlab.com/api/v4/projects/1/packages/nuget/query", "@type": "SearchQueryService", "comment": "Filter and search for packages by keyword."}' }
end
end
end
diff --git a/lib/api/entities/package.rb b/lib/api/entities/package.rb
index c92a4677220..ab6cc0fcb0a 100644
--- a/lib/api/entities/package.rb
+++ b/lib/api/entities/package.rb
@@ -26,7 +26,7 @@ module API
expose :status, documentation: { type: 'string', example: 'default' }
expose :_links do
- expose :web_path do |package|
+ expose :web_path, if: ->(package) { package.default? } do |package|
package_path(package)
end
diff --git a/lib/api/entities/packages/debian/distribution.rb b/lib/api/entities/packages/debian/distribution.rb
index 97a3c479f40..a11f4337f38 100644
--- a/lib/api/entities/packages/debian/distribution.rb
+++ b/lib/api/entities/packages/debian/distribution.rb
@@ -5,17 +5,18 @@ module API
module Packages
module Debian
class Distribution < Grape::Entity
- expose :id
- expose :codename
- expose :suite
- expose :origin
- expose :label
- expose :version
- expose :description
- expose :valid_time_duration_seconds
+ expose :id, documentation: { type: 'integer', example: 1 }
+ expose :codename, documentation: { type: 'string', example: 'unstable' }
+ expose :suite, documentation: { type: 'string', example: 'unstable' }
+ expose :origin, documentation: { type: 'string', example: 'Grep' }
+ expose :label, documentation: { type: 'string', example: 'grep.be' }
+ expose :version, documentation: { type: 'string', example: '12' }
+ expose :description, documentation: { type: 'string', example: 'My description' }
+ expose :valid_time_duration_seconds, documentation: { type: 'integer', example: 604800 }
- expose :component_names, as: :components
- expose :architecture_names, as: :architectures
+ expose :component_names, as: :components, documentation: { is_array: true, type: 'string', example: 'main' }
+ expose :architecture_names, as: :architectures,
+ documentation: { is_array: true, type: 'string', example: 'amd64' }
end
end
end
diff --git a/lib/api/entities/plan_limit.rb b/lib/api/entities/plan_limit.rb
index 34018f03eb1..d69be0077f2 100644
--- a/lib/api/entities/plan_limit.rb
+++ b/lib/api/entities/plan_limit.rb
@@ -17,6 +17,7 @@ module API
expose :maven_max_file_size, documentation: { type: 'integer', example: 3221225472 }
expose :npm_max_file_size, documentation: { type: 'integer', example: 524288000 }
expose :nuget_max_file_size, documentation: { type: 'integer', example: 524288000 }
+ expose :pipeline_hierarchy_size, documentation: { type: 'integer', example: 1000 }
expose :pypi_max_file_size, documentation: { type: 'integer', example: 3221225472 }
expose :terraform_module_max_file_size, documentation: { type: 'integer', example: 1073741824 }
expose :storage_size_limit, documentation: { type: 'integer', example: 15000 }
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 1c1bafbf161..37be6903d8b 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -81,6 +81,10 @@ module API
expose(:container_registry_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :container_registry) }
expose(:security_and_compliance_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
expose(:releases_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :releases) }
+ expose(:environments_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :environments) }
+ expose(:feature_flags_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :feature_flags) }
+ expose(:infrastructure_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :infrastructure) }
+ expose(:monitor_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :monitor) }
expose :emails_disabled, documentation: { type: 'boolean' }
expose :shared_runners_enabled, documentation: { type: 'boolean' }
diff --git a/lib/api/entities/project_integration.rb b/lib/api/entities/project_integration.rb
index 29bb60a19e5..f4709ce6dab 100644
--- a/lib/api/entities/project_integration.rb
+++ b/lib/api/entities/project_integration.rb
@@ -5,8 +5,8 @@ module API
class ProjectIntegration < Entities::ProjectIntegrationBasic
# Expose serialized properties
expose :properties, documentation: { type: 'Hash', example: { "token" => "secr3t" } } do |integration, options|
- integration.api_field_names.to_h do |name|
- [name, integration.public_send(name)] # rubocop:disable GitlabSecurity/PublicSend
+ integration.api_field_names.index_with do |name|
+ integration.public_send(name) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/api/entities/push_event_payload.rb b/lib/api/entities/push_event_payload.rb
index 6aad5f10177..2d8f0d9344c 100644
--- a/lib/api/entities/push_event_payload.rb
+++ b/lib/api/entities/push_event_payload.rb
@@ -3,8 +3,14 @@
module API
module Entities
class PushEventPayload < Grape::Entity
- expose :commit_count, :action, :ref_type, :commit_from, :commit_to, :ref,
- :commit_title, :ref_count
+ expose :commit_count, documentation: { type: 'integer', example: 1 }
+ expose :action, documentation: { type: 'string', example: 'pushed' }
+ expose :ref_type, documentation: { type: 'string', example: 'branch' }
+ expose :commit_from, documentation: { type: 'string', example: '50d4420237a9de7be1304607147aec22e4a14af7' }
+ expose :commit_to, documentation: { type: 'string', example: 'c5feabde2d8cd023215af4d2ceeb7a64839fc428' }
+ expose :ref, documentation: { type: 'string', example: 'master' }
+ expose :commit_title, documentation: { type: 'string', example: 'Add simple search to projects in public area' }
+ expose :ref_count, documentation: { type: 'integer', example: 1 }
end
end
end
diff --git a/lib/api/entities/ssh_key.rb b/lib/api/entities/ssh_key.rb
index 3db10bb8ec2..37e8ad7b1f5 100644
--- a/lib/api/entities/ssh_key.rb
+++ b/lib/api/entities/ssh_key.rb
@@ -12,6 +12,7 @@ module API
example: 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6Yjz\
GGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCdd\
NaP0L+hM7zhFNzjFvpaMgJw0=' }
+ expose :usage_type, documentation: { type: 'string', example: 'auth' }
end
end
end
diff --git a/lib/api/entities/ssh_signature.rb b/lib/api/entities/ssh_signature.rb
new file mode 100644
index 00000000000..dc3800c87c5
--- /dev/null
+++ b/lib/api/entities/ssh_signature.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class SshSignature < Grape::Entity
+ expose :verification_status, documentation: { type: 'string', example: 'unverified' }
+ expose :key, using: 'API::Entities::SSHKey'
+ end
+ end
+end
diff --git a/lib/api/entities/tag_signature.rb b/lib/api/entities/tag_signature.rb
new file mode 100644
index 00000000000..e75fd04109a
--- /dev/null
+++ b/lib/api/entities/tag_signature.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class TagSignature < Grape::Entity
+ expose :signature_type, documentation: { type: 'string', example: 'PGP' }
+
+ expose :signature, merge: true do |tag|
+ ::API::Entities::X509Signature.represent tag.signature if tag.signature_type == :X509
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/todo.rb b/lib/api/entities/todo.rb
index 5bbbb59f565..02dfdb68af9 100644
--- a/lib/api/entities/todo.rb
+++ b/lib/api/entities/todo.rb
@@ -32,6 +32,7 @@ module API
def todo_target_url(todo)
return design_todo_target_url(todo) if todo.for_design?
+ return todo.access_request_url if todo.member_access_requested?
target_type = todo.target_type.gsub('::', '_').underscore
target_url = "#{todo.resource_parent.class.to_s.underscore}_#{target_type}_url"
diff --git a/lib/api/events.rb b/lib/api/events.rb
index 0a0141484ef..d3e8892f3bc 100644
--- a/lib/api/events.rb
+++ b/lib/api/events.rb
@@ -15,8 +15,15 @@ module API
desc "List currently authenticated user's events" do
detail 'This feature was introduced in GitLab 9.3.'
success Entities::Event
+ is_array true
+ failure [
+ { code: 401, message: 'Unauthorized' }
+ ]
end
params do
+ optional :scope, type: String,
+ desc: 'Include all events across a user’s projects',
+ documentation: { example: 'all' }
use :pagination
use :event_filter_params
use :sort_params
@@ -32,12 +39,17 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID or Username of the user'
+ requires :id, type: String, desc: 'The ID or username of the user'
end
resource :users do
desc 'Get the contribution events of a specified user' do
detail 'This feature was introduced in GitLab 8.13.'
success Entities::Event
+ tags %w[events]
+ is_array true
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
end
params do
use :pagination
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 6b6f5cbfb3f..9142591aebd 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -9,6 +9,8 @@ module API
feature_category :feature_flags
urgency :low
+ BadValueError = Class.new(StandardError)
+
# TODO: remove these helpers with feature flag set_feature_flag_service
helpers do
def gate_value(params)
@@ -18,6 +20,8 @@ module API
when '0', 'false'
false
else
+ raise BadValueError unless params[:value].match? /^\d+(\.\d+)?$/
+
# https://github.com/jnunemaker/flipper/blob/master/lib/flipper/typecast.rb#L47
if params[:value].to_s.include?('.')
params[:value].to_f
@@ -153,7 +157,9 @@ module API
present Feature.get(params[:name]), # rubocop:disable Gitlab/AvoidFeatureGet
with: Entities::Feature, current_user: current_user
end
- rescue Feature::Target::UnknowTargetError => e
+ rescue BadValueError
+ bad_request!("Value must be boolean or numeric, got #{params[:value]}")
+ rescue Feature::Target::UnknownTargetError => e
bad_request!(e.message)
end
diff --git a/lib/api/files.rb b/lib/api/files.rb
index fa749299b9a..b02f1a8728b 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -172,14 +172,24 @@ module API
desc: 'The url encoded path to the file.', documentation: { example: 'lib%2Fclass%2Erb' }
optional :ref, type: String,
desc: 'The name of branch, tag or commit', allow_blank: false, documentation: { example: 'main' }
+ optional :lfs, type: Boolean,
+ desc: 'Retrieve binary data for a file that is an lfs pointer',
+ default: false
end
get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS, urgency: :low do
assign_file_vars!
- no_cache_headers
- set_http_headers(blob_data)
+ if params[:lfs] && @blob.stored_externally?
+ lfs_object = LfsObject.find_by_oid(@blob.lfs_oid)
+ not_found! unless lfs_object&.project_allowed_access?(@project)
+
+ present_carrierwave_file!(lfs_object.file)
+ else
+ no_cache_headers
+ set_http_headers(blob_data)
- send_git_blob @repo, @blob
+ send_git_blob @repo, @blob
+ end
end
desc 'Get file metadata from repository'
diff --git a/lib/api/freeze_periods.rb b/lib/api/freeze_periods.rb
index 40f1be83028..abd8f4c0b94 100644
--- a/lib/api/freeze_periods.rb
+++ b/lib/api/freeze_periods.rb
@@ -34,7 +34,7 @@ module API
get ":id/freeze_periods" do
authorize! :read_freeze_period, user_project
- freeze_periods = ::FreezePeriodsFinder.new(user_project, current_user).execute
+ freeze_periods = ::Ci::FreezePeriodsFinder.new(user_project, current_user).execute
present paginate(freeze_periods), with: Entities::FreezePeriod, current_user: current_user
end
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 3584f8d025a..da5b0930543 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -28,6 +28,13 @@ module API
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'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[generic_packages]
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
@@ -47,6 +54,17 @@ module API
desc 'Upload package file' do
detail 'This feature was introduced in GitLab 13.5'
+ success [
+ { code: 200 },
+ { code: 201 }
+ ]
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[generic_packages]
end
params do
@@ -88,6 +106,13 @@ module API
desc 'Download package file' do
detail 'This feature was introduced in GitLab 13.5'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[generic_packages]
end
params do
diff --git a/lib/api/group_debian_distributions.rb b/lib/api/group_debian_distributions.rb
index 1f43bb0e2b3..0364e2e7b56 100644
--- a/lib/api/group_debian_distributions.rb
+++ b/lib/api/group_debian_distributions.rb
@@ -3,7 +3,7 @@
module API
class GroupDebianDistributions < ::API::Base
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the group'
end
before do
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index ca99e30fbf7..23db10dbdbf 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -100,7 +100,7 @@ module API
options = {
with: serializer,
current_user: current_user,
- statistics: params[:statistics] && current_user&.admin?
+ statistics: params[:statistics] && current_user&.can_read_all_resources?
}
groups = groups.with_statistics if options[:statistics]
@@ -186,7 +186,7 @@ module API
end
def check_subscription!(group)
- render_api_error!("This group can't be removed because it is linked to a subscription.", :bad_request) if group.paid?
+ render_api_error!("This group can't be removed because it is linked to a subscription.", :bad_request) if group.prevent_delete?
end
end
@@ -195,6 +195,8 @@ module API
desc 'Get a groups list' do
success Entities::Group
+ is_array true
+ tags %w[groups]
end
params do
use :group_list_params
@@ -207,6 +209,7 @@ module API
desc 'Create a group. Available only for users who can create groups.' do
success Entities::Group
+ tags %w[groups]
end
params do
requires :name, type: String, desc: 'The name of the group'
@@ -240,6 +243,7 @@ module API
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Update a group. Available only for users who can administrate groups.' do
success Entities::Group
+ tags %w[groups]
end
params do
optional :name, type: String, desc: 'The name of the group'
@@ -265,6 +269,7 @@ module API
desc 'Get a single group, with containing projects.' do
success Entities::GroupDetail
+ tags %w[groups]
end
params do
use :with_custom_attributes
@@ -278,7 +283,9 @@ module API
present_group_details(params, group, with_projects: params[:with_projects])
end
- desc 'Remove a group.'
+ desc 'Remove a group.' do
+ tags %w[groups]
+ end
delete ":id", feature_category: :subgroups, urgency: :low do
group = find_group!(params[:id])
authorize! :admin_group, group
@@ -289,6 +296,8 @@ module API
desc 'Get a list of projects in this group.' do
success Entities::Project
+ is_array true
+ tags %w[groups]
end
params do
optional :archived, type: Boolean, desc: 'Limit by archived status'
@@ -329,6 +338,8 @@ module API
desc 'Get a list of shared projects in this group' do
success Entities::Project
+ is_array true
+ tags %w[groups]
end
params do
optional :archived, type: Boolean, desc: 'Limit by archived status'
@@ -357,6 +368,8 @@ module API
desc 'Get a list of subgroups in this group.' do
success Entities::Group
+ is_array true
+ tags %w[groups]
end
params do
use :group_list_params
@@ -369,6 +382,8 @@ module API
desc 'Get a list of descendant groups of this group.' do
success Entities::Group
+ is_array true
+ tags %w[groups]
end
params do
use :group_list_params
@@ -382,6 +397,7 @@ module API
desc 'Transfer a project to the group namespace. Available only for admin.' do
success Entities::GroupDetail
+ tags %w[groups]
end
params do
requires :project_id, type: String, desc: 'The ID or path of the project'
@@ -400,7 +416,11 @@ module API
end
end
- desc 'Get the groups to where the current group can be transferred to'
+ desc 'Get the groups to where the current group can be transferred to' do
+ success Entities::Group
+ is_array true
+ tags %w[groups]
+ end
params do
optional :search, type: String, desc: 'Return list of namespaces matching the search criteria'
use :pagination
@@ -415,7 +435,9 @@ module API
present_groups params, groups, serializer: Entities::PublicGroupDetails
end
- desc 'Transfer a group to a new parent group or promote a subgroup to a root group'
+ desc 'Transfer a group to a new parent group or promote a subgroup to a root group' do
+ tags %w[groups]
+ end
params do
optional :group_id,
type: Integer,
@@ -440,6 +462,7 @@ module API
desc 'Share a group with a group' do
success Entities::GroupDetail
+ tags %w[groups]
end
params do
requires :group_id, type: Integer, desc: 'The ID of the group to share'
diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb
index fa2537bcfc4..8260d8a88f8 100644
--- a/lib/api/helm_packages.rb
+++ b/lib/api/helm_packages.rb
@@ -32,15 +32,21 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID or full path of a project'
+ requires :id, types: [Integer, String], desc: 'The ID or full path of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/helm', requirements: HELM_REQUIREMENTS do
desc 'Download a chart index' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
end
get ":channel/index.yaml" do
@@ -56,10 +62,17 @@ module API
desc 'Download a chart' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
- requires :file_name, type: String, desc: 'Helm package file name'
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
+ requires :file_name, type: String, desc: 'Helm package file name', documentation: { example: 'mychart' }
end
get ":channel/charts/:file_name.tgz" do
project = authorized_user_project(action: :read_package)
@@ -67,16 +80,23 @@ module API
package_file = Packages::Helm::PackageFilesFinder.new(project, params[:channel], file_name: "#{params[:file_name]}.tgz").most_recent!
- track_package_event('pull_package', :helm, project: project, namespace: project.namespace)
+ track_package_event('pull_package', :helm, project: project, namespace: project.namespace, property: 'i_package_helm_user')
present_package_file!(package_file)
end
desc 'Authorize a chart upload from workhorse' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
end
post "api/:channel/charts/authorize" do
authorize_workhorse!(
@@ -88,9 +108,16 @@ module API
desc 'Upload a chart' do
detail 'This feature was introduced in GitLab 14.0'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[helm_packages]
end
params do
- requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex
+ requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex, documentation: { example: 'stable' }
requires :chart, type: ::API::Validations::Types::WorkhorseFile, desc: 'The chart file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
end
post "api/:channel/charts" do
@@ -110,7 +137,8 @@ module API
package, chart_params.merge(build: current_authenticated_job)
).execute
- track_package_event('push_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace)
+ track_package_event('push_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace,
+ property: 'i_package_helm_user')
::Packages::Helm::ExtractionWorker.perform_async(params[:channel], chart_package_file.id) # rubocop:disable CodeReuse/Worker
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 75e7612bd5b..0b5a471ea12 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -167,6 +167,10 @@ module API
current_authenticated_job.project == project
end
+ def enforce_jobs_api_rate_limits(project)
+ ::Feature.enabled?(:ci_enforce_rate_limits_jobs_api, project)
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def find_group(id)
if id.to_s =~ INTEGER_ID_REGEX
@@ -301,7 +305,7 @@ module API
def authenticated_as_admin!
authenticate!
- forbidden! unless current_user.admin?
+ forbidden! unless current_user.can_admin_all_resources?
end
def authorize!(action, subject = :global, reason = nil)
@@ -710,7 +714,7 @@ module API
unauthorized! unless initial_current_user
- unless initial_current_user.admin?
+ unless initial_current_user.can_admin_all_resources?
forbidden!('Must be admin to use sudo')
end
diff --git a/lib/api/helpers/award_emoji.rb b/lib/api/helpers/award_emoji.rb
index 3ea35381c97..f8417366ea4 100644
--- a/lib/api/helpers/award_emoji.rb
+++ b/lib/api/helpers/award_emoji.rb
@@ -7,7 +7,7 @@ module API
[
{ type: 'issue', resource: :projects, find_by: :iid, feature_category: :team_planning },
{ type: 'merge_request', resource: :projects, find_by: :iid, feature_category: :code_review },
- { type: 'snippet', resource: :projects, find_by: :id, feature_category: :snippets }
+ { type: 'snippet', resource: :projects, find_by: :id, feature_category: :source_code_management }
]
end
@@ -18,18 +18,16 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def awardable
@awardable ||=
- begin
- if params.include?(:note_id)
- note_id = params.delete(:note_id)
+ if params.include?(:note_id)
+ note_id = params.delete(:note_id)
- awardable.notes.find(note_id)
- elsif params.include?(:issue_iid)
- user_project.issues.find_by!(iid: params[:issue_iid])
- elsif params.include?(:merge_request_iid)
- user_project.merge_requests.find_by!(iid: params[:merge_request_iid])
- elsif params.include?(:snippet_id)
- user_project.snippets.find(params[:snippet_id])
- end
+ awardable.notes.find(note_id)
+ elsif params.include?(:issue_iid)
+ user_project.issues.find_by!(iid: params[:issue_iid])
+ elsif params.include?(:merge_request_iid)
+ user_project.merge_requests.find_by!(iid: params[:merge_request_iid])
+ elsif params.include?(:snippet_id)
+ user_project.snippets.find(params[:snippet_id])
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/helpers/discussions_helpers.rb b/lib/api/helpers/discussions_helpers.rb
index c94199b17bc..182ada54a12 100644
--- a/lib/api/helpers/discussions_helpers.rb
+++ b/lib/api/helpers/discussions_helpers.rb
@@ -8,7 +8,7 @@ module API
# extend it.
{
Issue => :team_planning,
- Snippet => :snippets,
+ Snippet => :source_code_management,
MergeRequest => :code_review,
Commit => :code_review
}
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index 99273e81730..543449c0349 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -415,14 +415,6 @@ module API
desc: 'The URL of the external wiki'
}
],
- 'flowdock' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'Flowdock token'
- }
- ],
'hangouts-chat' => [
{
required: true,
@@ -893,7 +885,6 @@ module API
::Integrations::EmailsOnPush,
::Integrations::Ewm,
::Integrations::ExternalWiki,
- ::Integrations::Flowdock,
::Integrations::HangoutsChat,
::Integrations::Harbor,
::Integrations::Irker,
diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb
index eed9fa30d3c..ee3bb49c97f 100644
--- a/lib/api/helpers/merge_requests_helpers.rb
+++ b/lib/api/helpers/merge_requests_helpers.rb
@@ -11,100 +11,107 @@ module API
params :ee_approval_params do
end
- params :merge_requests_negatable_params do
- optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
- optional :author_username, type: String, desc: 'Return merge requests which are authored by the user with the given username'
+ params :merge_requests_negatable_params do |options|
+ optional :author_id, type: Integer,
+ desc: "#{options[:prefix]}Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`."
+ optional :author_username, type: String,
+ desc: "#{options[:prefix]}Returns merge requests created by the given `username`. Mutually exclusive with `author_id`."
mutually_exclusive :author_id, :author_username
-
- optional :assignee_id,
- types: [Integer, String],
- integer_none_any: true,
- desc: 'Return merge requests which are assigned to the user with the given ID'
- optional :assignee_username,
- type: Array[String],
- check_assignees_count: true,
- coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
- desc: 'Return merge requests which are assigned to the user with the given username'
+ optional :assignee_id, types: [Integer, String],
+ integer_none_any: true,
+ desc: "#{options[:prefix]}Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee."
+ optional :assignee_username, type: Array[String],
+ check_assignees_count: true,
+ coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
+ desc: "#{options[:prefix]}Returns merge requests created by the given `username`. Mutually exclusive with `author_id`.",
+ documentation: { is_array: true }
mutually_exclusive :assignee_id, :assignee_username
- optional :reviewer_username,
- type: String,
- desc: 'Return merge requests which have the user as a reviewer with the given username'
-
- optional :labels,
- type: Array[String],
- coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
- desc: 'Comma-separated list of label names'
- optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
- optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
+ optional :reviewer_username, type: String,
+ desc: "#{options[:prefix]}Returns merge requests which have the user as a reviewer with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. Introduced in GitLab 13.8."
+ optional :labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: "#{options[:prefix]}Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive.",
+ documentation: { is_array: true }
+ optional :milestone, type: String,
+ desc: "#{options[:prefix]}Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone."
+ optional :my_reaction_emoji, type: String,
+ desc: "#{options[:prefix]}Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction."
end
params :merge_requests_base_params do
- use :merge_requests_negatable_params
- optional :reviewer_id,
- types: [Integer, String],
- integer_none_any: true,
- desc: 'Return merge requests which have the user as a reviewer with the given ID'
+ use :merge_requests_negatable_params, prefix: ''
+
+ optional :reviewer_id, types: [Integer, String],
+ integer_none_any: true,
+ desc: 'Returns merge requests which have the user as a reviewer with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.'
mutually_exclusive :reviewer_id, :reviewer_username
- optional :state,
- type: String,
- values: %w[opened closed locked merged all],
- default: 'all',
- desc: 'Return opened, closed, locked, merged, or all merge requests'
- optional :order_by,
- type: String,
- values: Helpers::MergeRequestsHelpers.sort_options,
- default: 'created_at',
- desc: "Return merge requests ordered by #{Helpers::MergeRequestsHelpers.sort_options_help} fields."
- optional :sort,
- type: String,
- values: %w[asc desc],
- default: 'desc',
- desc: 'Return merge requests sorted in `asc` or `desc` order.'
- optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
- optional :with_merge_status_recheck, type: Boolean, desc: 'Request that stale merge statuses be rechecked asynchronously', default: false
- optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
- optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
- optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time'
- optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time'
- optional :view,
- type: String,
- values: %w[simple],
- desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
-
- optional :scope,
- type: String,
- values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
- desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
- optional :source_branch, type: String, desc: 'Return merge requests with the given source branch'
- optional :source_project_id, type: Integer, desc: 'Return merge requests with the given source project id'
- optional :target_branch, type: String, desc: 'Return merge requests with the given target branch'
- optional :search,
- type: String,
- desc: 'Search merge requests for text present in the title, description, or any combination of these'
- optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
- optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
- optional :not, type: Hash, desc: 'Parameters to negate' do
- use :merge_requests_negatable_params
- optional :reviewer_id,
- types: Integer,
- desc: 'Return merge requests which have the user as a reviewer with the given ID'
+ optional :state, type: String,
+ values: %w[opened closed locked merged all],
+ default: 'all',
+ desc: 'Returns `all` merge requests or just those that are `opened`, `closed`, `locked`, or `merged`.'
+ optional :order_by, type: String,
+ values: Helpers::MergeRequestsHelpers.sort_options,
+ default: 'created_at',
+ desc: "Returns merge requests ordered by #{Helpers::MergeRequestsHelpers.sort_options_help} fields. Introduced in GitLab 14.8."
+ optional :sort, type: String,
+ values: %w[asc desc],
+ default: 'desc',
+ desc: 'Returns merge requests sorted in `asc` or `desc` order.'
+ optional :with_labels_details, type: Boolean,
+ default: false,
+ desc: 'If `true`, response returns more details for each label in labels field: `:name`,`:color`, `:description`, `:description_html`, `:text_color`'
+ optional :with_merge_status_recheck, type: Boolean,
+ default: false,
+ desc: 'If `true`, this projection requests (but does not guarantee) that the `merge_status` field be recalculated asynchronously. Introduced in GitLab 13.0.'
+ optional :created_after, type: DateTime,
+ desc: 'Returns merge requests created on or after the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :created_before, type: DateTime,
+ desc: 'Returns merge requests created on or before the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :updated_after, type: DateTime,
+ desc: 'Returns merge requests updated on or after the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :updated_before, type: DateTime,
+ desc: 'Returns merge requests updated on or before the given time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :view, type: String,
+ values: %w[simple],
+ desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
+ optional :scope, type: String,
+ values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
+ desc: 'Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
+ optional :source_branch, type: String, desc: 'Returns merge requests with the given source branch'
+ optional :source_project_id, type: Integer, desc: 'Returns merge requests with the given source project id'
+ optional :target_branch, type: String, desc: 'Returns merge requests with the given target branch'
+ optional :search, type: String,
+ desc: 'Search merge requests against their `title` and `description`.'
+ optional :in, type: String,
+ desc: 'Modify the scope of the search attribute. `title`, `description`, or a string joining them with comma.',
+ documentation: { example: 'title,description' }
+ optional :wip, type: String,
+ values: %w[yes no],
+ desc: 'Filter merge requests against their `wip` status. `yes` to return only draft merge requests, `no` to return non-draft merge requests.'
+ optional :not, type: Hash, desc: 'Returns merge requests that do not match the parameters supplied' do
+ use :merge_requests_negatable_params, prefix: '`<Negated>` '
+
+ optional :reviewer_id, types: Integer,
+ desc: '`<Negated>` Returns merge requests which have the user as a reviewer with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.'
mutually_exclusive :reviewer_id, :reviewer_username
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'
+ optional :deployed_before, desc: 'Returns merge requests deployed before the given date/time. Expected in ISO 8601 format.',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :deployed_after, desc: 'Returns merge requests deployed after the given date/time. Expected in ISO 8601 format',
+ documentation: { example: '2019-03-15T08:00:00Z' }
+ optional :environment, desc: 'Returns merge requests deployed to the given environment',
+ documentation: { example: '2019-03-15T08:00:00Z' }
end
params :optional_scope_param do
- optional :scope,
- type: String,
- values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
- default: 'created_by_me',
- desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
+ optional :scope, type: String,
+ values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
+ default: 'created_by_me',
+ desc: 'Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
end
def handle_merge_request_errors!(merge_request)
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index 45671b09be9..302dac4abf7 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -9,7 +9,7 @@ module API
{
Issue => :team_planning,
MergeRequest => :code_review,
- Snippet => :snippets
+ Snippet => :source_code_management
}
end
@@ -90,7 +90,12 @@ module API
params = finder_params_by_noteable_type_and_id(noteable_type, noteable_id)
noteable = NotesFinder.new(current_user, params).target
- noteable = nil unless can?(current_user, noteable_read_ability_name(noteable), noteable)
+
+ # Checking `read_note` permission here, because API code does not seem to use NoteFinder to find notes,
+ # but rather pulls notes directly through notes association, so there is no chance to check read_note
+ # permission at service level. With WorkItem model we need to make sure that it has WorkItem::Widgets::Note
+ # available in order to access notes.
+ noteable = nil unless can_read_notes?(noteable)
noteable || not_found!(noteable_type)
end
@@ -147,6 +152,13 @@ module API
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/211538')
end
+
+ private
+
+ def can_read_notes?(noteable)
+ Ability.allowed?(current_user, noteable_read_ability_name(noteable), noteable) &&
+ Ability.allowed?(current_user, :read_note, noteable)
+ end
end
end
end
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index a9d91895cfe..3ea558f3569 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -47,14 +47,14 @@ module API
end
def recipe_upload_urls
- { upload_urls: file_names.select(&method(:recipe_file?)).to_h do |file_name|
- [file_name, build_recipe_file_upload_url(file_name)]
+ { upload_urls: file_names.select(&method(:recipe_file?)).index_with do |file_name|
+ build_recipe_file_upload_url(file_name)
end }
end
def package_upload_urls
- { upload_urls: file_names.select(&method(:package_file?)).to_h do |file_name|
- [file_name, build_package_file_upload_url(file_name)]
+ { upload_urls: file_names.select(&method(:package_file?)).index_with do |file_name|
+ build_package_file_upload_url(file_name)
end }
end
@@ -128,7 +128,7 @@ module API
strong_memoize(:project) do
case package_scope
when :project
- find_project!(params[:id])
+ user_project(action: :read_package)
when :instance
full_path = ::Packages::Conan::Metadatum.full_path_from(package_username: params[:package_username])
find_project!(full_path)
diff --git a/lib/api/helpers/packages/dependency_proxy_helpers.rb b/lib/api/helpers/packages/dependency_proxy_helpers.rb
index 1ae863a5a25..4b0e63c8f3b 100644
--- a/lib/api/helpers/packages/dependency_proxy_helpers.rb
+++ b/lib/api/helpers/packages/dependency_proxy_helpers.rb
@@ -19,7 +19,9 @@ module API
def redirect_registry_request(forward_to_registry: false, package_type: nil, target: nil, **options)
if forward_to_registry && redirect_registry_request_available?(package_type, target) && maven_forwarding_ff_enabled?(package_type, target)
::Gitlab::Tracking.event(self.options[:for].name, "#{package_type}_request_forward")
- redirect(registry_url(package_type, options))
+ redirect(registry_url(package_type, options), body: options[:body])
+ # For the requests with POST methods we need to set status 307 in order to keep request's method
+ status :temporary_redirect if options[:method] == 'POST'
else
yield
end
@@ -32,7 +34,7 @@ module API
case package_type
when :npm
- "#{base_url}#{options[:package_name]}"
+ "#{base_url}#{[options[:path], options[:package_name]].compact.join('/')}"
when :pypi
"#{base_url}#{options[:package_name]}/"
when :maven
diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb
index 96a10d43401..8d913268405 100644
--- a/lib/api/helpers/packages_helpers.rb
+++ b/lib/api/helpers/packages_helpers.rb
@@ -78,10 +78,18 @@ module API
end
end
- def track_package_event(event_name, scope, **args)
- ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute
+ def track_package_event(action, scope, **args)
+ ::Packages::CreateEventService.new(nil, current_user, event_name: action, scope: scope).execute
category = args.delete(:category) || self.options[:for].name
- ::Gitlab::Tracking.event(category, event_name.to_s, **args)
+ event_name = "i_package_#{scope}_user"
+ ::Gitlab::Tracking.event(
+ category,
+ action.to_s,
+ property: event_name,
+ label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly',
+ context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context],
+ **args
+ )
end
def present_package_file!(package_file, supports_direct_download: true)
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index c95bf0f0c21..9d370176e62 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -37,6 +37,10 @@ module API
optional :container_registry_access_level, type: String, values: %w(disabled private enabled), desc: 'Controls visibility of the container registry. One of `disabled`, `private` or `enabled`. `private` will make the container registry accessible only to project members (reporter role and above). `enabled` will make the container registry accessible to everyone who has access to the project. `disabled` will disable the container registry'
optional :security_and_compliance_access_level, type: String, values: %w(disabled private enabled), desc: 'Security and compliance access level. One of `disabled`, `private` or `enabled`'
optional :releases_access_level, type: String, values: %w(disabled private enabled), desc: 'Releases access level. One of `disabled`, `private` or `enabled`'
+ optional :environments_access_level, type: String, values: %w(disabled private enabled), desc: 'Environments access level. One of `disabled`, `private` or `enabled`'
+ optional :feature_flags_access_level, type: String, values: %w(disabled private enabled), desc: 'Feature flags access level. One of `disabled`, `private` or `enabled`'
+ optional :infrastructure_access_level, type: String, values: %w(disabled private enabled), desc: 'Infrastructure access level. One of `disabled`, `private` or `enabled`'
+ optional :monitor_access_level, type: String, values: %w(disabled private enabled), desc: 'Monitor access level. One of `disabled`, `private` or `enabled`'
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
@@ -183,6 +187,10 @@ module API
:mr_default_target_self,
:enforce_auth_checks_on_uploads,
:releases_access_level,
+ :environments_access_level,
+ :feature_flags_access_level,
+ :infrastructure_access_level,
+ :monitor_access_level,
# TODO: remove in API v5, replaced by *_access_level
:issues_enabled,
diff --git a/lib/api/integrations/jira_connect/subscriptions.rb b/lib/api/integrations/jira_connect/subscriptions.rb
index a6e931ba7bb..cc2199e0ef6 100644
--- a/lib/api/integrations/jira_connect/subscriptions.rb
+++ b/lib/api/integrations/jira_connect/subscriptions.rb
@@ -11,14 +11,22 @@ module API
namespace :integrations do
namespace :jira_connect do
resource :subscriptions do
- desc 'Subscribe a namespace to a JiraConnectInstallation'
+ desc 'Subscribe a namespace to a JiraConnectInstallation' do
+ detail 'Subscribes the namespace to the JiraConnectInstallation'
+ success ::API::Entities::BasicSuccess
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[jira_connect_subscriptions]
+ end
params do
requires :jwt, type: String, desc: 'JWT token for authorization with the Jira Connect installation'
requires :namespace_path, type: String, desc: 'Path for the namespace that should be subscribed'
end
post do
- not_found! unless Feature.enabled?(:jira_connect_oauth, current_user)
-
jwt = Atlassian::JiraConnect::Jwt::Symmetric.new(params[:jwt])
installation = JiraConnectInstallation.find_by_client_key(jwt.iss_claim)
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index c4464666020..dbd5c5f9db1 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -191,7 +191,7 @@ module API
get '/authorized_keys', feature_category: :source_code_management, urgency: :high do
fingerprint = Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint_sha256
- key = Key.find_by_fingerprint_sha256(fingerprint)
+ key = Key.auth.find_by_fingerprint_sha256(fingerprint)
not_found!('Key') if key.nil?
present key, with: Entities::SSHKey
end
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index d06d1e9862a..777d5019a29 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -6,7 +6,6 @@ module API
class Kubernetes < ::API::Base
include Gitlab::Utils::StrongMemoize
- feature_category :kubernetes_management
before do
check_feature_enabled
authenticate_gitlab_kas_request!
@@ -86,7 +85,7 @@ module API
detail 'Retrieves agent info for the given token'
end
route_setting :authentication, cluster_agent_token_allowed: true
- get '/agent_info', urgency: :low do
+ get '/agent_info', feature_category: :kubernetes_management, urgency: :low do
project = agent.project
status 200
@@ -104,7 +103,7 @@ module API
detail 'Retrieves project info (if authorized)'
end
route_setting :authentication, cluster_agent_token_allowed: true
- get '/project_info', urgency: :low do
+ get '/project_info', feature_category: :kubernetes_management, urgency: :low do
project = find_project(params[:id])
not_found! unless agent_has_access_to_project?(project)
@@ -127,7 +126,7 @@ module API
requires :agent_id, type: Integer, desc: 'ID of the configured Agent'
requires :agent_config, type: JSON, desc: 'Configuration for the Agent'
end
- post '/' do
+ post '/', feature_category: :kubernetes_management do
agent = ::Clusters::Agent.find(params[:agent_id])
::Clusters::Agents::RefreshAuthorizationService.new(agent, config: params[:agent_config]).execute
@@ -147,10 +146,10 @@ module API
end
optional :unique_counters, type: Hash do
- optional :agent_users_using_ci_tunnel, type: Set[Integer], desc: 'A set of user ids that have interacted a CI Tunnel to'
+ optional :agent_users_using_ci_tunnel, type: Array[Integer], desc: 'An array of user ids that have interacted with CI Tunnel'
end
end
- post '/' do
+ post '/', feature_category: :kubernetes_management do
increment_count_events
increment_unique_events
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index 276560f3433..f348e20cc0b 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -35,6 +35,11 @@ module API
context[:skip_project_check] = true
end
+ # Disable comments in markdown for IE browsers because comments in IE
+ # could allow script execution.
+ browser = Browser.new(headers['User-Agent'])
+ context[:allow_comments] = !browser.ie?
+
present({ html: Banzai.render_and_post_process(params[:text], context) }, with: Entities::Markdown)
end
end
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index 30cdaba76ba..411a53a481b 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -107,7 +107,7 @@ module API
def fetch_package(file_name:, project: nil, group: nil)
order_by_package_file = file_name.include?(::Packages::Maven::Metadata.filename) &&
- !params[:path].include?(::Packages::Maven::FindOrCreatePackageService::SNAPSHOT_TERM)
+ !params[:path].include?(::Packages::Maven::FindOrCreatePackageService::SNAPSHOT_TERM)
::Packages::Maven::PackageFinder.new(
current_user,
@@ -150,10 +150,17 @@ module API
desc 'Download the maven package file at instance level' do
detail 'This feature was introduced in GitLab 11.6'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name'
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get 'packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -190,14 +197,24 @@ module API
desc 'Download the maven package file at a group level' do
detail 'This feature was introduced in GitLab 11.7'
+ success [
+ { code: 200 },
+ { code: 302 }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name'
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/-/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -225,10 +242,20 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Download the maven package file' do
detail 'This feature was introduced in GitLab 11.3'
+ success [
+ { code: 200 },
+ { code: 302 }
+ ]
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name'
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -250,10 +277,18 @@ module API
desc 'Workhorse authorize the maven package file upload' do
detail 'This feature was introduced in GitLab 11.3'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex, documentation: { example: 'mypkg-1.0-SNAPSHOT.pom' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
put ':id/packages/maven/*path/:file_name/authorize', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
@@ -266,10 +301,19 @@ module API
desc 'Upload the maven package file' do
detail 'This feature was introduced in GitLab 11.3'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' },
+ { code: 422, message: 'Unprocessable Entity' }
+ ]
+ tags %w[maven_packages]
end
params do
- requires :path, type: String, desc: 'Package path'
- requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex
+ requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
+ requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex, documentation: { example: 'mypkg-1.0-SNAPSHOT.pom' }
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
diff --git a/lib/api/members.rb b/lib/api/members.rb
index f4e38207aca..76f4364106b 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -20,6 +20,8 @@ module API
resource source_type.pluralize, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Gets a list of group or project members viewable by the authenticated user.' do
success Entities::Member
+ is_array true
+ tags %w[members]
end
params do
optional :query, type: String, desc: 'A query string to search for members'
@@ -42,6 +44,8 @@ module API
desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do
success Entities::Member
+ is_array true
+ tags %w[members]
end
params do
optional :query, type: String, desc: 'A query string to search for members'
@@ -63,6 +67,7 @@ module API
desc 'Gets a member of a group or project.' do
success Entities::Member
+ tags %w[members]
end
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
@@ -82,6 +87,7 @@ module API
desc 'Gets a member of a group or project, including those who gained membership through ancestor group' do
success Entities::Member
+ tags %w[members]
end
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
@@ -101,6 +107,7 @@ module API
desc 'Adds a member to a group or project.' do
success Entities::Member
+ tags %w[members]
end
params do
requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)'
@@ -126,6 +133,7 @@ module API
desc 'Updates a member of a group or project.' do
success Entities::Member
+ tags %w[members]
end
params do
requires :user_id, type: Integer, desc: 'The user ID of the new member'
@@ -153,7 +161,9 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
- desc 'Removes a user from a group or project.'
+ desc 'Removes a user from a group or project.' do
+ tags %w[members]
+ end
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
optional :skip_subresources, type: Boolean, default: false,
diff --git a/lib/api/merge_request_approvals.rb b/lib/api/merge_request_approvals.rb
index 7622ec717cc..35fdcfe3ab0 100644
--- a/lib/api/merge_request_approvals.rb
+++ b/lib/api/merge_request_approvals.rb
@@ -88,6 +88,24 @@ module API
present_approval(merge_request)
end
+
+ desc 'Remove all merge request approvals' do
+ detail 'Clear all approvals of merge request. This feature was added in GitLab 15.4'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
+ end
+ put 'reset_approvals', urgency: :low do
+ merge_request = find_project_merge_request(params[:merge_request_iid])
+
+ unauthorized! unless current_user.can?(:reset_merge_request_approvals, merge_request)
+
+ merge_request.approvals.delete_all
+
+ status :accepted
+ end
end
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index bb2861aa221..a9572cf7ce6 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -134,7 +134,13 @@ module API
resource :merge_requests do
desc 'List merge requests' do
+ detail 'Get all merge requests the authenticated user has access to. By default it returns only merge requests created by the current user. To get all merge requests, use parameter `scope=all`.'
success Entities::MergeRequestBasic
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
use :merge_requests_params
@@ -150,16 +156,24 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, type: String, desc: 'The ID or URL-encoded path of the group owned by the authenticated user.'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Get a list of group merge requests' do
+ desc 'List group merge requests' do
+ detail 'Get all merge requests for this group and its subgroups.'
success Entities::MergeRequestBasic
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
use :merge_requests_params
- optional :non_archived, type: Boolean, desc: 'Return merge requests from non archived projects',
- default: true
+ optional :non_archived, type: Boolean,
+ default: true,
+ desc: 'Returns merge requests from non archived projects only.'
end
get ":id/merge_requests", feature_category: :code_review, urgency: :low do
validate_anonymous_search_access! if declared_params[:search].present?
@@ -170,36 +184,62 @@ module API
end
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project.'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
include TimeTrackingEndpoints
helpers do
params :optional_params do
- optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
- optional :assignee_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Comma-separated list of assignee ids'
- optional :reviewer_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Comma-separated list of reviewer ids'
- optional :description, type: String, desc: 'The description of the merge request'
- optional :labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
- optional :add_labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
- optional :remove_labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
- optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
- optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
- optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch'
+ optional :assignee_id, type: Integer, desc: 'Assignee user ID.'
+ optional :assignee_ids, type: Array[Integer],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ desc: 'The IDs of the users to assign the merge request to, as a comma-separated list. Set to 0 or provide an empty value to unassign all assignees.',
+ documentation: { is_array: true }
+ optional :reviewer_ids, type: Array[Integer],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ desc: 'The IDs of the users to review the merge request, as a comma-separated list. Set to 0 or provide an empty value to unassign all reviewers.',
+ documentation: { is_array: true }
+ optional :description, type: String, desc: 'Description of the merge request. Limited to 1,048,576 characters.'
+ optional :labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'Comma-separated label names for a merge request. Set to an empty string to unassign all labels.',
+ documentation: { is_array: true }
+ optional :add_labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'Comma-separated label names to add to a merge request.',
+ documentation: { is_array: true }
+ optional :remove_labels, type: Array[String],
+ coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'Comma-separated label names to remove from a merge request.',
+ documentation: { is_array: true }
+ optional :milestone_id, type: Integer, desc: 'The global ID of a milestone to assign the merge reques to.'
+ optional :remove_source_branch, type: Boolean, desc: 'Flag indicating if a merge request should remove the source branch when merging.'
+ optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch.'
optional :allow_maintainer_to_push, type: Boolean, as: :allow_collaboration, desc: '[deprecated] See allow_collaboration'
- optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
+ optional :squash, type: Grape::API::Boolean, desc: 'Squash commits into a single commit when merging.'
use :optional_params_ee
end
end
- desc 'List merge requests' do
+ desc 'List project merge requests' do
+ detail 'Get all merge requests for this project.'
success Entities::MergeRequestBasic
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
use :merge_requests_params
- optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IID array of merge requests'
+
+ optional :iids, type: Array[Integer],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ desc: 'Returns the request having the given `iid`.',
+ documentation: { is_array: true }
end
get ":id/merge_requests", feature_category: :code_review, urgency: :low do
authorize! :read_merge_request, user_project
@@ -226,15 +266,24 @@ module API
**options
end
- desc 'Create a merge request' do
+ desc 'Create merge request' do
+ detail 'Create a new merge request.'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
success Entities::MergeRequest
+ tags %w[merge_requests]
end
params do
- requires :title, type: String, desc: 'The title of the merge request'
- requires :source_branch, type: String, desc: 'The source branch'
- requires :target_branch, type: String, desc: 'The target branch'
+ requires :title, type: String, desc: 'The title of the merge request.'
+ requires :source_branch, type: String, desc: 'The source branch.'
+ requires :target_branch, type: String, desc: 'The target branch.'
optional :target_project_id, type: Integer,
- desc: 'The target project of the merge request defaults to the :id of the project'
+ desc: 'The target project of the merge request defaults to the :id of the project.'
use :optional_params
end
post ":id/merge_requests", feature_category: :code_review, urgency: :low do
@@ -253,9 +302,17 @@ module API
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
- desc 'Delete a merge request'
+ desc 'Delete a merge request' do
+ detail 'Only for administrators and project owners. Deletes the merge request in question. '
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 412, message: 'Precondition failed' }
+ ]
+ tags %w[merge_requests]
+ end
params do
- requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
+ requires :merge_request_iid, type: Integer, desc: 'The internal ID of the merge request.'
end
delete ":id/merge_requests/:merge_request_iid", feature_category: :code_review, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -268,13 +325,19 @@ module API
end
params do
- requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
- optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
- optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch'
- optional :include_rebase_in_progress, type: Boolean, desc: 'Returns whether a rebase operation is ongoing '
+ requires :merge_request_iid, type: Integer, desc: 'The internal ID of the merge request.'
+ optional :render_html, type: Boolean, desc: 'If `true`, response includes rendered HTML for title and description.'
+ optional :include_diverged_commits_count, type: Boolean, desc: 'If `true`, response includes the commits behind the target branch.'
+ optional :include_rebase_in_progress, type: Boolean, desc: 'If `true`, response includes whether a rebase operation is in progress.'
end
- desc 'Get a single merge request' do
+ desc 'Get single merge request' do
+ detail 'Shows information about a single merge request. Note: the `changes_count` value in the response is a string, not an integer. This is because when an merge request has too many changes to display and store, it is capped at 1,000. In that case, the API returns the string `"1000+"` for the changes count.'
+
success Entities::MergeRequest
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -289,8 +352,13 @@ module API
include_rebase_in_progress: params[:include_rebase_in_progress]
end
- desc 'Get the participants of a merge request' do
+ desc 'Get single merge request participants' do
+ detail 'Get a list of merge request participants.'
success Entities::UserBasic
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -300,8 +368,13 @@ module API
present paginate(participants), with: Entities::UserBasic
end
- desc 'Get the reviewers of a merge request' do
+ desc 'Get single merge request reviewers' do
+ detail 'Get a list of merge request reviewers.'
success Entities::MergeRequestReviewer
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/reviewers', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -311,8 +384,13 @@ module API
present paginate(reviewers), with: Entities::MergeRequestReviewer
end
- desc 'Get the commits of a merge request' do
+ desc 'Get single merge request commits' do
+ detail 'Get a list of merge request commits.'
success Entities::Commit
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/commits', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -324,8 +402,13 @@ module API
present commits, with: Entities::Commit
end
- desc 'Get the context commits of a merge request' do
+ desc 'List merge request context commits' do
+ detail 'Get a list of merge request context commits.'
success Entities::Commit
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review, urgency: :high do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -336,10 +419,20 @@ module API
end
params do
- requires :commits, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, allow_blank: false, desc: 'List of context commits sha'
- end
- desc 'create context commits of merge request' do
+ requires :commits, type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ allow_blank: false,
+ desc: 'The context commits’ SHA.',
+ documentation: { is_array: true }
+ end
+ desc 'Create merge request context commits' do
+ detail 'Create a list of merge request context commits.'
success Entities::Commit
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
post ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
commit_ids = params[:commits]
@@ -363,9 +456,21 @@ module API
end
params do
- requires :commits, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, allow_blank: false, desc: 'List of context commits sha'
+ requires :commits, type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ allow_blank: false,
+ desc: 'The context commits’ SHA.',
+ documentation: { is_array: true }
+ end
+ desc 'Delete merge request context commits' do
+ detail 'Delete a list of merge request context commits.'
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
- desc 'remove context commits of merge request'
delete ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
commit_ids = params[:commits]
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -382,8 +487,13 @@ module API
status 204
end
- desc 'Show the merge request changes' do
+ desc 'Get single merge request changes' do
+ detail 'Shows information about the merge request including its files and changes.'
success Entities::MergeRequestChanges
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/changes', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
@@ -395,17 +505,46 @@ module API
access_raw_diffs: to_boolean(params.fetch(:access_raw_diffs, false))
end
- desc 'Get the merge request pipelines' do
+ desc 'Get the merge request diffs' do
+ detail 'Get a list of merge request diffs.'
+ success Entities::Diff
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
+ end
+ params do
+ use :pagination
+ end
+ get ':id/merge_requests/:merge_request_iid/diffs', feature_category: :code_review, urgency: :low do
+ merge_request = find_merge_request_with_access(params[:merge_request_iid])
+
+ present paginate(merge_request.merge_request_diff.paginated_diffs(params[:page], params[:per_page])).diffs, with: Entities::Diff
+ end
+
+ desc 'Get single merge request pipelines' do
+ detail 'Get a list of merge request pipelines.'
success Entities::Ci::PipelineBasic
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
get ':id/merge_requests/:merge_request_iid/pipelines', urgency: :low, feature_category: :continuous_integration do
pipelines = merge_request_pipelines_with_access
-
present paginate(pipelines), with: Entities::Ci::PipelineBasic
end
- desc 'Create a pipeline for merge request' do
+ desc 'Create merge request pipeline' do
+ detail 'Create a new pipeline for a merge request. A pipeline created via this endpoint doesn’t run a regular branch/tag pipeline. It requires `.gitlab-ci.yml` to be configured with `only: [merge_requests]` to create jobs.'
success ::API::Entities::Ci::Pipeline
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 404, message: 'Not found' },
+ { code: 405, message: 'Method not allowed' }
+ ]
+ tags %w[merge_requests]
end
post ':id/merge_requests/:merge_request_iid/pipelines', urgency: :low, feature_category: :continuous_integration do
pipeline = ::MergeRequests::CreatePipelineService
@@ -423,15 +562,25 @@ module API
end
end
- desc 'Update a merge request' do
+ desc 'Update merge request' do
+ detail 'Updates an existing merge request. You can change the target branch, title, or even close the merge request.'
success Entities::MergeRequest
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
- optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
- optional :target_branch, type: String, allow_blank: false, desc: 'The target branch'
- optional :state_event, type: String, values: %w[close reopen],
- desc: 'Status of the merge request'
- optional :discussion_locked, type: Boolean, desc: 'Whether the MR discussion is locked'
+ optional :title, type: String, allow_blank: false, desc: 'The title of the merge request.'
+ optional :target_branch, type: String, allow_blank: false, desc: 'The target branch.'
+ optional :state_event, type: String,
+ values: %w[close reopen],
+ desc: 'New state (close/reopen).'
+ optional :discussion_locked, type: Boolean,
+ desc: 'Flag indicating if the merge request’s discussion is locked. If the discussion is locked only project members can add, edit or resolve comments.'
use :optional_params
at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
@@ -456,17 +605,27 @@ module API
end
desc 'Merge a merge request' do
+ detail 'Accept and merge changes submitted with the merge request using this API.'
success Entities::MergeRequest
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 405, message: 'Method not allowed' },
+ { code: 409, message: 'Conflict' },
+ { code: 422, message: 'Unprocessable entity' }
+ ]
+ tags %w[merge_requests]
end
params do
- optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
- optional :squash_commit_message, type: String, desc: 'Custom squash commit message'
+ optional :merge_commit_message, type: String, desc: 'Custom merge commit message.'
+ optional :squash_commit_message, type: String, desc: 'Custom squash commit message.'
optional :should_remove_source_branch, type: Boolean,
- desc: 'When true, the source branch will be deleted if possible'
+ desc: 'If `true`, removes the source branch.'
optional :merge_when_pipeline_succeeds, type: Boolean,
- desc: 'When true, this merge request will be merged when the pipeline succeeds'
- optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
- optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
+ desc: 'If `true`, the merge request is merged when the pipeline succeeds.'
+ optional :sha, type: String, desc: 'If present, then this SHA must match the HEAD of the source branch, otherwise the merge fails.'
+ optional :squash, type: Grape::API::Boolean, desc: 'If `true`, the commits are squashed into a single commit on merge.'
end
put ':id/merge_requests/:merge_request_iid/merge', feature_category: :code_review, urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/4796')
@@ -512,7 +671,13 @@ module API
end
end
- desc 'Returns the up to date merge-ref HEAD commit'
+ desc 'Returns the up to date merge-ref HEAD commit' do
+ detail 'Returns the up to date merge-ref HEAD commit'
+ failure [
+ { code: 400, message: 'Bad request' }
+ ]
+ tags %w[merge_requests]
+ end
get ':id/merge_requests/:merge_request_iid/merge_ref', feature_category: :code_review do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -525,8 +690,16 @@ module API
end
end
- desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
+ desc 'Cancel Merge When Pipeline Succeeds' do
+ detail 'Cancel merge if "Merge When Pipeline Succeeds" is enabled'
success Entities::MergeRequest
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' },
+ { code: 405, message: 'Method not allowed' },
+ { code: 406, message: 'Not acceptable' }
+ ]
+ tags %w[merge_requests]
end
post ':id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds', feature_category: :code_review do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -536,11 +709,17 @@ module API
AutoMergeService.new(merge_request.target_project, current_user).cancel(merge_request)
end
- desc 'Rebase the merge request against its target branch' do
- detail 'This feature was added in GitLab 11.6'
+ desc 'Rebase a merge request' do
+ detail 'Automatically rebase the `source_branch` of the merge request against its `target_branch`. This feature was added in GitLab 11.6'
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' }
+ ]
+ tags %w[merge_requests]
end
params do
- optional :skip_ci, type: Boolean, desc: 'Do not create CI pipeline'
+ optional :skip_ci, type: Boolean, desc: 'Set to true to skip creating a CI pipeline.'
end
put ':id/merge_requests/:merge_request_iid/rebase', feature_category: :code_review, urgency: :low do
merge_request = find_project_merge_request(params[:merge_request_iid])
@@ -554,22 +733,13 @@ module API
rescue ::MergeRequest::RebaseLockTimeout => e
render_api_error!(e.message, 409)
end
-
- desc 'Remove merge request approvals' do
- detail 'This feature was added in GitLab 15.4'
- end
- put ':id/merge_requests/:merge_request_iid/reset_approvals', feature_category: :code_review, urgency: :low do
- merge_request = find_project_merge_request(params[:merge_request_iid])
-
- unauthorized! unless current_user.bot? && merge_request.eligible_for_approval_by?(current_user)
-
- merge_request.approvals.delete_all
-
- status :accepted
- end
-
- desc 'List issues that will be closed on merge' do
+ desc 'List issues that close on merge' do
+ detail 'Get all the issues that would be closed by merging the provided merge request.'
success Entities::MRNote
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[merge_requests]
end
params do
use :pagination
diff --git a/lib/api/ml/mlflow.rb b/lib/api/ml/mlflow.rb
index 56bfac1530e..54bbe0ee465 100644
--- a/lib/api/ml/mlflow.rb
+++ b/lib/api/ml/mlflow.rb
@@ -126,14 +126,31 @@ module API
end
params do
requires :name, type: String, desc: 'Experiment name'
+ optional :tags, type: Array, desc: 'Tags with information about the experiment'
optional :artifact_location, type: String, desc: 'This will be ignored'
- optional :tags, type: Array, desc: 'This will be ignored'
end
post 'create', urgency: :low do
- present experiment_repository.create!(params[:name]), with: Entities::Ml::Mlflow::NewExperiment
+ present experiment_repository.create!(params[:name], params[:tags]),
+ with: Entities::Ml::Mlflow::NewExperiment
rescue ActiveRecord::RecordInvalid
resource_already_exists!
end
+
+ desc 'Sets a tag for an experiment.' do
+ summary 'Sets a tag for an experiment. '
+
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#set-experiment-tag'
+ end
+ params do
+ requires :experiment_id, type: String, desc: 'ID of the experiment.'
+ requires :key, type: String, desc: 'Name for the tag.'
+ requires :value, type: String, desc: 'Value for the tag.'
+ end
+ post 'set-experiment-tag', urgency: :low do
+ bad_request! unless experiment_repository.add_tag!(experiment, params[:key], params[:value])
+
+ {}
+ end
end
resource :runs do
@@ -148,10 +165,10 @@ module API
desc: 'Unix timestamp in milliseconds of when the run started.',
default: 0
optional :user_id, type: String, desc: 'This will be ignored'
- optional :tags, type: Array, desc: 'This will be ignored'
+ optional :tags, type: Array, desc: 'Tags are stored, but not displayed'
end
post 'create', urgency: :low do
- present candidate_repository.create!(experiment, params[:start_time]),
+ present candidate_repository.create!(experiment, params[:start_time], params[:tags]),
with: Entities::Ml::Mlflow::Run, packages_url: packages_url
end
@@ -229,6 +246,22 @@ module API
{}
end
+ desc 'Sets a tag for a run.' do
+ summary 'Sets a tag for a run. '
+
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#set-tag'
+ end
+ params do
+ requires :run_id, type: String, desc: 'UUID of the run.'
+ requires :key, type: String, desc: 'Name for the tag.'
+ requires :value, type: String, desc: 'Value for the tag.'
+ end
+ post 'set-tag', urgency: :low do
+ bad_request! unless candidate_repository.add_tag!(candidate, params[:key], params[:value])
+
+ {}
+ end
+
desc 'Logs multiple parameters and metrics.' do
summary 'Log a batch of metrics and params for a run. Validation errors will block the entire batch, '\
'duplicate errors will be ignored.'
@@ -251,6 +284,7 @@ module API
post 'log-batch', urgency: :low do
candidate_repository.add_metrics(candidate, params[:metrics])
candidate_repository.add_params(candidate, params[:params])
+ candidate_repository.add_tags(candidate, params[:tags])
{}
end
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index eeb66c86b3b..2b1007e715a 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -6,6 +6,8 @@ module API
before { authenticate! }
+ NAMESPACES_TAGS = %w[namespaces].freeze
+
helpers do
params :optional_list_params_ee do
# EE::API::Namespaces would override this helper
@@ -20,12 +22,18 @@ module API
prepend_mod_with('API::Namespaces') # rubocop: disable Cop/InjectEnterpriseEditionModule
resource :namespaces do
- desc 'Get a namespaces list' do
+ desc 'List namespaces' do
+ detail 'Get a list of the namespaces of the authenticated user. If the user is an administrator, a list of all namespaces in the GitLab instance is shown.'
success Entities::Namespace
+ failure [
+ { code: 401, message: 'Unauthorized' }
+ ]
+ is_array true
+ tags NAMESPACES_TAGS
end
params do
- optional :search, type: String, desc: "Search query for namespaces"
- optional :owned_only, type: Boolean, desc: "Owned namespaces only"
+ optional :search, type: String, desc: 'Returns a list of namespaces the user is authorized to view based on the search criteria'
+ optional :owned_only, type: Boolean, desc: 'In GitLab 14.2 and later, returns a list of owned namespaces only'
use :pagination
use :optional_list_params_ee
@@ -46,11 +54,17 @@ module API
present paginate(namespaces), options.reverse_merge(custom_namespace_present_options)
end
- desc 'Get a namespace by ID' do
+ desc 'Get namespace by ID' do
+ detail 'Get a namespace by ID'
success Entities::Namespace
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags NAMESPACES_TAGS
end
params do
- requires :id, type: String, desc: "Namespace's ID or path"
+ requires :id, types: [String, Integer], desc: 'ID or URL-encoded path of the namespace'
end
get ':id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS, feature_category: :subgroups, urgency: :low do
user_namespace = find_namespace!(params[:id])
@@ -58,12 +72,17 @@ module API
present user_namespace, with: Entities::Namespace, current_user: current_user
end
- desc 'Get existence of a namespace including alternative suggestions' do
+ desc 'Get existence of a namespace' do
+ detail 'Get existence of a namespace by path. Suggests a new namespace path that does not already exist.'
success Entities::NamespaceExistence
+ failure [
+ { code: 401, message: 'Unauthorized' }
+ ]
+ tags NAMESPACES_TAGS
end
params do
- requires :namespace, type: String, desc: "Namespace's path"
- optional :parent_id, type: Integer, desc: "The ID of the parent namespace. If no ID is specified, only top-level namespaces are considered."
+ requires :namespace, type: String, desc: "Namespace’s path"
+ optional :parent_id, type: Integer, desc: 'The ID of the parent namespace. If no ID is specified, only top-level namespaces are considered.'
end
get ':namespace/exists', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS, feature_category: :subgroups, urgency: :low do
check_rate_limit!(:namespace_exists, scope: current_user)
diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb
index 494b493f5e0..f42ded5ac09 100644
--- a/lib/api/npm_project_packages.rb
+++ b/lib/api/npm_project_packages.rb
@@ -16,6 +16,12 @@ module API
namespace 'projects/:id/packages/npm' do
desc 'Download the NPM tarball' do
detail 'This feature was introduced in GitLab 11.8'
+ success code: 200
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
params do
requires :package_name, type: String, desc: 'Package name'
@@ -33,13 +39,21 @@ module API
package_file = ::Packages::PackageFileFinder
.new(package, params[:file_name]).execute!
- track_package_event('pull_package', package, category: 'API::NpmPackages', project: project, namespace: project.namespace)
+ track_package_event('pull_package', :npm, category: 'API::NpmPackages', project: project, namespace: project.namespace)
present_package_file!(package_file)
end
desc 'Create NPM package' do
detail 'This feature was introduced in GitLab 11.8'
+ success code: 200
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[npm_packages]
end
params do
requires :package_name, type: String, desc: 'Package name'
diff --git a/lib/api/nuget_group_packages.rb b/lib/api/nuget_group_packages.rb
index eb55e4cbf70..c93b24ee544 100644
--- a/lib/api/nuget_group_packages.rb
+++ b/lib/api/nuget_group_packages.rb
@@ -45,7 +45,7 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX
+ requires :id, types: [Integer, String], desc: 'The group ID or full group path.', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
index d549a8be035..aa517661791 100644
--- a/lib/api/nuget_project_packages.rb
+++ b/lib/api/nuget_project_packages.rb
@@ -39,18 +39,19 @@ module API
end
def project_or_group
- authorized_user_project
+ authorized_user_project(action: :read_package)
end
def snowplow_gitlab_standard_context
- { project: authorized_user_project, namespace: authorized_user_project.namespace }
+ { project: project_or_group, namespace: project_or_group.namespace }
end
def authorize_nuget_upload
+ project = project_or_group
authorize_workhorse!(
- subject: project_or_group,
+ subject: project,
has_length: false,
- maximum_size: project_or_group.actual_limits.nuget_max_file_size
+ maximum_size: project.actual_limits.nuget_max_file_size
)
end
@@ -67,8 +68,9 @@ module API
end
def upload_nuget_package_file(symbol_package: false)
- authorize_upload!(project_or_group)
- bad_request!('File is too large') if project_or_group.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
+ project = project_or_group
+ authorize_upload!(project)
+ bad_request!('File is too large') if project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
file_params = params.merge(
file: params[:package],
@@ -76,7 +78,7 @@ module API
)
package = ::Packages::CreateTemporaryPackageService.new(
- project_or_group, current_user, declared_params.merge(build: current_authenticated_job)
+ project, current_user, declared_params.merge(build: current_authenticated_job)
).execute(:nuget, name: temp_file_name(symbol_package))
package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job))
@@ -100,6 +102,14 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/package-publish-resource
desc 'The NuGet Package Publish endpoint' do
detail 'This feature was introduced in GitLab 12.6'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
params do
@@ -121,6 +131,17 @@ module API
forbidden!
end
+
+ desc 'The NuGet Package Authorize endpoint' do
+ detail 'This feature was introduced in GitLab 14.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
+ end
put 'authorize', urgency: :low do
authorize_nuget_upload
end
@@ -128,8 +149,15 @@ module API
# https://docs.microsoft.com/en-us/nuget/api/symbol-package-publish-resource
desc 'The NuGet Symbol Package Publish endpoint' do
detail 'This feature was introduced in GitLab 14.1'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
-
params do
use :file_params
end
@@ -149,13 +177,24 @@ module API
forbidden!
end
+
+ desc 'The NuGet Symbol Package Authorize endpoint' do
+ detail 'This feature was introduced in GitLab 14.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
+ end
put 'symbolpackage/authorize', urgency: :low do
authorize_nuget_upload
end
# https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
end
namespace '/download/*package_name' do
after_validation do
@@ -164,6 +203,13 @@ module API
desc 'The NuGet Content Service - index request' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200, model: ::API::Entities::Nuget::PackagesVersions
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
get 'index', format: :json, urgency: :low do
present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages(params[:package_name])),
@@ -172,10 +218,17 @@ module API
desc 'The NuGet Content Service - content request' do
detail 'This feature was introduced in GitLab 12.8'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[nuget_packages]
end
params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
- requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: '1.3.0.17' }
+ requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
end
get '*package_version/*package_filename', format: [:nupkg, :snupkg], urgency: :low do
filename = "#{params[:package_filename]}.#{params[:format]}"
diff --git a/lib/api/pages.rb b/lib/api/pages.rb
index 7e230bd3c67..0cedf7d975f 100644
--- a/lib/api/pages.rb
+++ b/lib/api/pages.rb
@@ -10,11 +10,18 @@ module API
end
params do
- requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer],
+ desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Unpublish pages' do
- detail 'This feature was introduced in GitLab 12.6'
+ detail 'Remove pages. The user must have administrator access. This feature was introduced in GitLab 12.6'
+ success code: 204
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pages]
end
delete ':id/pages' do
authorize! :remove_pages, user_project
diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb
index c5add42decc..e5e6ccdf025 100644
--- a/lib/api/project_container_repositories.rb
+++ b/lib/api/project_container_repositories.rb
@@ -20,9 +20,15 @@ module API
end
route_setting :authentication, job_token_allowed: true, job_token_scope: :project
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Get a project container repositories' do
+ desc 'List container repositories within a project' do
detail 'This feature was introduced in GitLab 11.8.'
success Entities::ContainerRegistry::Repository
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[container_registry]
end
params do
use :pagination
@@ -41,6 +47,13 @@ module API
desc 'Delete repository' do
detail 'This feature was introduced in GitLab 11.8.'
+ success status: :accepted, message: 'Success'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -49,18 +62,20 @@ module API
authorize_admin_container_image!
repository.delete_scheduled!
- unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
- DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) # rubocop:disable CodeReuse/Worker
- end
-
track_package_event('delete_repository', :container, user: current_user, project: user_project, namespace: user_project.namespace)
status :accepted
end
- desc 'Get a list of repositories tags' do
+ desc 'List tags of a repository' do
detail 'This feature was introduced in GitLab 11.8.'
success Entities::ContainerRegistry::Tag
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -77,6 +92,13 @@ module API
desc 'Delete repository tags (in bulk)' do
detail 'This feature was introduced in GitLab 11.8.'
+ success status: :accepted, message: 'Success'
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -104,9 +126,15 @@ module API
status :accepted
end
- desc 'Get a details about repository tag' do
+ desc 'Get details about a repository tag' do
detail 'This feature was introduced in GitLab 11.8.'
success Entities::ContainerRegistry::TagDetails
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
@@ -121,6 +149,13 @@ module API
desc 'Delete repository tag' do
detail 'This feature was introduced in GitLab 11.8.'
+ success status: :ok, message: 'Success'
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[container_registry]
end
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb
index d09c481403f..158ba7465f4 100644
--- a/lib/api/project_packages.rb
+++ b/lib/api/project_packages.rb
@@ -17,9 +17,15 @@ module API
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Get all project packages' do
+ desc 'Get a list of project packages' do
detail 'This feature was introduced in GitLab 11.8'
- success ::API::Entities::Package
+ success code: 200, model: ::API::Entities::Package
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Project Not Found' }
+ ]
+ is_array true
+ tags %w[project_packages]
end
params do
use :pagination
@@ -48,7 +54,12 @@ module API
desc 'Get a single project package' do
detail 'This feature was introduced in GitLab 11.9'
- success ::API::Entities::Package
+ success code: 200, model: ::API::Entities::Package
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[project_packages]
end
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
@@ -58,11 +69,19 @@ module API
package = ::Packages::PackageFinder
.new(user_project, params[:package_id]).execute
+ render_api_error!('Package not found', 404) unless package.default?
+
present package, with: ::API::Entities::Package, user: current_user, namespace: user_project.namespace
end
- desc 'Remove a package' do
+ desc 'Delete a project package' do
detail 'This feature was introduced in GitLab 11.9'
+ success code: 204
+ failure [
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[project_packages]
end
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 93ffb23fea8..7ef722301ca 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -6,7 +6,7 @@ module API
before { check_snippets_enabled }
- feature_category :snippets
+ feature_category :source_code_management
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index fc898c30a71..de39419b70b 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -207,7 +207,10 @@ module API
resource :users, requirements: API::USER_REQUIREMENTS do
desc 'Get a user projects' do
- success Entities::BasicProjectDetails
+ success code: 200, model: Entities::BasicProjectDetails
+ failure [{ code: 404, message: '404 User Not Found' }]
+ tags %w[projects]
+ is_array true
end
params do
requires :user_id, type: String, desc: 'The ID or username of the user'
@@ -225,7 +228,10 @@ module API
end
desc 'Get projects starred by a user' do
- success Entities::BasicProjectDetails
+ success code: 200, model: Entities::BasicProjectDetails
+ failure [{ code: 404, message: '404 User Not Found' }]
+ tags %w[projects]
+ is_array true
end
params do
requires :user_id, type: String, desc: 'The ID or username of the user'
@@ -245,7 +251,9 @@ module API
include CustomAttributesEndpoints
desc 'Get a list of visible projects for authenticated user' do
- success Entities::BasicProjectDetails
+ success code: 200, model: Entities::BasicProjectDetails
+ tags %w[projects]
+ is_array true
end
params do
use :collection_params
@@ -258,12 +266,18 @@ module API
end
desc 'Create new project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' },
+ { code: 400, message: 'Bad request' }
+ ]
+ tags %w[projects]
end
params do
- optional :name, type: String, desc: 'The name of the project'
- optional :path, type: String, desc: 'The path of the repository'
- optional :default_branch, type: String, desc: 'The default branch of the project'
+ optional :name, type: String, desc: 'The name of the project', documentation: { example: 'New Project' }
+ optional :path, type: String, desc: 'The path of the repository', documentation: { example: 'new_project' }
+ optional :default_branch, type: String, desc: 'The default branch of the project', documentation: { example: 'main' }
at_least_one_of :name, :path
use :optional_create_project_params
use :create_params
@@ -295,13 +309,19 @@ module API
end
desc 'Create new project for a specified user. Only available to admin users.' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' },
+ { code: 400, message: 'Bad request' }
+ ]
+ tags %w[projects]
end
params do
- requires :name, type: String, desc: 'The name of the project'
- requires :user_id, type: Integer, desc: 'The ID of a user'
- optional :path, type: String, desc: 'The path of the repository'
- optional :default_branch, type: String, desc: 'The default branch of the project'
+ requires :name, type: String, desc: 'The name of the project', documentation: { example: 'New Project' }
+ requires :user_id, type: Integer, desc: 'The ID of a user', documentation: { example: 1 }
+ optional :path, type: String, desc: 'The path of the repository', documentation: { example: 'new_project' }
+ optional :default_branch, type: String, desc: 'The default branch of the project', documentation: { example: 'main' }
use :optional_project_params
use :optional_create_project_params
use :create_params
@@ -339,7 +359,8 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get a single project' do
- success Entities::ProjectWithAccess
+ success code: 200, model: Entities::ProjectWithAccess
+ tags %w[projects]
end
params do
use :statistics_params
@@ -364,15 +385,21 @@ module API
end
desc 'Fork new project for the current user or provided namespace.' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' },
+ { code: 409, message: 'Conflict' }
+ ]
+ tags %w[projects]
end
params do
- optional :namespace, type: String, desc: '(deprecated) The ID or name of the namespace that the project will be forked into'
- optional :namespace_id, type: Integer, desc: 'The ID of the namespace that the project will be forked into'
- optional :namespace_path, type: String, desc: 'The path of the namespace that the project will be forked into'
- optional :path, type: String, desc: 'The path that will be assigned to the fork'
- optional :name, type: String, desc: 'The name that will be assigned to the fork'
- optional :description, type: String, desc: 'The description that will be assigned to the fork'
+ optional :namespace, type: String, desc: '(deprecated) The ID or name of the namespace that the project will be forked into', documentation: { example: 'gitlab' }
+ optional :namespace_id, type: Integer, desc: 'The ID of the namespace that the project will be forked into', documentation: { example: 1 }
+ optional :namespace_path, type: String, desc: 'The path of the namespace that the project will be forked into', documentation: { example: 'new_path/gitlab' }
+ optional :path, type: String, desc: 'The path that will be assigned to the fork', documentation: { example: 'fork' }
+ optional :name, type: String, desc: 'The name that will be assigned to the fork', documentation: { example: 'Fork' }
+ optional :description, type: String, desc: 'The description that will be assigned to the fork', documentation: { example: 'Description' }
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork'
optional :mr_default_target_self, type: Boolean, desc: 'Merge requests of this forked project targets itself by default'
end
@@ -410,7 +437,9 @@ module API
end
desc 'List forks of this project' do
- success Entities::Project
+ success code: 200, model: Entities::Project
+ tags %w[projects]
+ is_array true
end
params do
use :collection_params
@@ -422,19 +451,30 @@ module API
present_projects forks, request_scope: user_project
end
- desc 'Check pages access of this project'
+ desc 'Check pages access of this project' do
+ success code: 200
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
+ end
get ':id/pages_access', urgency: :low, feature_category: :pages do
authorize! :read_pages_content, user_project unless user_project.public_pages?
status 200
end
desc 'Update an existing project' do
- success Entities::Project
+ success code: 200, model: Entities::Project
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
params do
- optional :name, type: String, desc: 'The name of the project'
- optional :default_branch, type: String, desc: 'The default branch of the project'
- optional :path, type: String, desc: 'The path of the repository'
+ optional :name, type: String, desc: 'The name of the project', documentation: { example: 'project' }
+ optional :default_branch, type: String, desc: 'The default branch of the project', documentation: { example: 'main' }
+ optional :path, type: String, desc: 'The path of the repository', documentation: { example: 'group/project' }
use :optional_project_params
use :optional_update_params
@@ -466,7 +506,11 @@ module API
end
desc 'Archive a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/archive', feature_category: :projects do
authorize!(:archive_project, user_project)
@@ -477,7 +521,11 @@ module API
end
desc 'Unarchive a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/unarchive', feature_category: :projects, urgency: :default do
authorize!(:archive_project, user_project)
@@ -488,7 +536,12 @@ module API
end
desc 'Star a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 304, message: 'Not modified' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/star', feature_category: :projects do
if current_user.starred?(user_project)
@@ -502,7 +555,12 @@ module API
end
desc 'Unstar a project' do
- success Entities::Project
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 304, message: 'Not modified' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/unstar', feature_category: :projects do
if current_user.starred?(user_project)
@@ -516,10 +574,16 @@ module API
end
desc 'Get the users who starred a project' do
- success Entities::UserBasic
+ success code: 200, model: Entities::UserBasic
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
end
params do
- optional :search, type: String, desc: 'Return list of users matching the search criteria'
+ optional :search, type: String, desc: 'Return list of users matching the search criteria', documentation: { example: 'user' }
use :pagination
end
get ':id/starrers', feature_category: :projects do
@@ -528,23 +592,44 @@ module API
present paginate(starrers), with: Entities::UserStarsProject
end
- desc 'Get languages in project repository'
+ desc 'Get languages in project repository' do
+ success code: 200
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
+ end
get ':id/languages', feature_category: :source_code_management, urgency: :medium do
::Projects::RepositoryLanguagesService
.new(user_project, current_user)
.execute.to_h { |lang| [lang.name, lang.share] }
end
- desc 'Delete a project'
+ desc 'Delete a project' do
+ success code: 202
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
delete ":id", feature_category: :projects do
authorize! :remove_project, user_project
delete_project(user_project)
end
- desc 'Mark this project as forked from another'
+ desc 'Mark this project as forked from another' do
+ success code: 201, model: Entities::Project
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
- requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
+ requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from', documentation: { example: 'gitlab' }
end
post ":id/fork/:forked_from_id", feature_category: :source_code_management do
authorize! :admin_project, user_project
@@ -559,12 +644,20 @@ module API
if result
present_project user_project.reset, with: Entities::Project, current_user: current_user
- else
- render_api_error!("Project already forked", 409) if user_project.forked?
+ elsif user_project.forked?
+ render_api_error!("Project already forked", 409)
end
end
- desc 'Remove a forked_from relationship'
+ desc 'Remove a forked_from relationship' do
+ success code: 204
+ failure [
+ { code: 304, message: 'Not modified' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
delete ":id/fork", feature_category: :source_code_management do
authorize! :remove_fork_project, user_project
@@ -576,10 +669,16 @@ module API
end
desc 'Share the project with a group' do
- success Entities::ProjectGroupLink
+ success code: 201, model: Entities::ProjectGroupLink
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
end
params do
- requires :group_id, type: Integer, desc: 'The ID of a group'
+ requires :group_id, type: Integer, desc: 'The ID of a group', documentation: { example: 1 }
requires :group_access, type: Integer, values: Gitlab::Access.values, as: :link_group_access, desc: 'The group access level'
optional :expires_at, type: Date, desc: 'Share expiration date'
end
@@ -601,6 +700,14 @@ module API
end
end
+ desc 'Remove a group share' do
+ success code: 204
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
requires :group_id, type: Integer, desc: 'The ID of the group'
end
@@ -619,6 +726,12 @@ module API
desc 'Import members from another project' do
detail 'This feature was introduced in GitLab 14.2'
+ success code: 201
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
end
params do
requires :project_id, type: Integer, desc: 'The ID of the source project to import the members from.'
@@ -642,6 +755,11 @@ module API
desc 'Workhorse authorize the file upload' do
detail 'This feature was introduced in GitLab 13.11'
+ success code: 200
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
end
post ':id/uploads/authorize', feature_category: :not_owned do # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
require_gitlab_workhorse!
@@ -651,7 +769,13 @@ module API
FileUploader.workhorse_authorize(has_length: false, maximum_size: project_attachment_size(user_project))
end
- desc 'Upload a file'
+ desc 'Upload a file' do
+ success code: 201, model: Entities::ProjectUpload
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded', documentation: { type: 'file' }
end
@@ -666,10 +790,16 @@ module API
end
desc 'Get the users list of a project' do
- success Entities::UserBasic
+ success code: 200, model: Entities::UserBasic
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
end
params do
- optional :search, type: String, desc: 'Return list of users matching the search criteria'
+ optional :search, type: String, desc: 'Return list of users matching the search criteria', documentation: { example: 'user' }
optional :skip_users, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Filter out users with the specified IDs'
use :pagination
end
@@ -683,10 +813,16 @@ module API
end
desc 'Get ancestor and shared groups for a project' do
- success Entities::PublicGroupDetails
+ success code: 200, model: Entities::PublicGroupDetails
+ failure [
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
+ tags %w[projects]
end
params do
- optional :search, type: String, desc: 'Return list of groups matching the search criteria'
+ optional :search, type: String, desc: 'Return list of groups matching the search criteria', documentation: { example: 'group' }
optional :skip_groups, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of group ids to exclude from list'
optional :with_shared, type: Boolean, default: false,
desc: 'Include shared groups'
@@ -705,6 +841,13 @@ module API
desc 'Start the housekeeping task for a project' do
detail 'This feature was introduced in GitLab 9.0.'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 409, message: 'Conflict' }
+ ]
+ tags %w[projects]
end
post ':id/housekeeping', feature_category: :source_code_management do
authorize_admin_project
@@ -718,6 +861,12 @@ module API
desc 'Start a task to recalculate repository size for a project' do
detail 'This feature was introduced in GitLab 15.0.'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
post ':id/repository_size', feature_category: :source_code_management do
authorize_admin_project
@@ -727,9 +876,17 @@ module API
::Projects::UpdateStatisticsService.new(user_project, nil, statistics: [:repository_size, :lfs_objects_size]).execute
end
- desc 'Transfer a project to a new namespace'
+ desc 'Transfer a project to a new namespace' do
+ success code: 200, model: Entities::Project
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 403, message: 'Unauthenticated' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[projects]
+ end
params do
- requires :namespace, type: String, desc: 'The ID or path of the new namespace'
+ requires :namespace, type: String, desc: 'The ID or path of the new namespace', documentation: { example: 'gitlab' }
end
put ":id/transfer", feature_category: :projects do
authorize! :change_namespace, user_project
@@ -744,9 +901,16 @@ module API
end
end
- desc 'Get the namespaces to where the project can be transferred'
+ desc 'Get the namespaces to where the project can be transferred' do
+ success code: 200, model: Entities::PublicGroupDetails
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ is_array true
+ tags %w[projects]
+ end
params do
- optional :search, type: String, desc: 'Return list of namespaces matching the search criteria'
+ optional :search, type: String, desc: 'Return list of namespaces matching the search criteria', documentation: { example: 'search' }
use :pagination
end
get ":id/transfer_locations", feature_category: :projects do
@@ -761,7 +925,11 @@ module API
end
desc 'Show the storage information' do
- success Entities::ProjectRepositoryStorage
+ success code: 200, model: Entities::ProjectRepositoryStorage
+ failure [
+ { code: 403, message: 'Unauthenticated' }
+ ]
+ tags %w[projects]
end
params do
requires :id, type: String, desc: 'ID of a project'
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index 6c649483da1..f9470ce1cb6 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -32,12 +32,12 @@ module API
helpers do
params :package_download do
- requires :file_identifier, type: String, desc: 'The PyPi package file identifier', file_path: true
- requires :sha256, type: String, desc: 'The PyPi package sha256 check sum'
+ requires :file_identifier, type: String, desc: 'The PyPi package file identifier', file_path: true, documentation: { example: 'my.pypi.package-0.0.1.tar.gz' }
+ requires :sha256, type: String, desc: 'The PyPi package sha256 check sum', documentation: { example: '5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff' }
end
params :package_name do
- requires :package_name, type: String, file_path: true, desc: 'The PyPi package name'
+ requires :package_name, type: String, file_path: true, desc: 'The PyPi package name', documentation: { example: 'my.pypi.package' }
end
def present_simple_index(group_or_project)
@@ -102,7 +102,7 @@ module API
end
params do
- requires :id, type: String, desc: 'The ID of a group'
+ requires :id, types: [Integer, String], desc: 'The ID or full path of the group.'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
after_validation do
@@ -110,6 +110,16 @@ module API
end
namespace ':id/-/packages/pypi' do
+ desc 'Download a package file from a group' do
+ detail 'This feature was introduced in GitLab 13.12'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
+ end
params do
use :package_download
end
@@ -123,13 +133,20 @@ module API
package = Packages::Pypi::PackageFinder.new(current_user, group, { filename: filename, sha256: params[:sha256] }).execute
package_file = ::Packages::PackageFileFinder.new(package, filename, with_file_name_like: false).execute
- track_package_event('pull_package', :pypi)
+ track_package_event('pull_package', :pypi, namespace: group, project: package.project)
present_package_file!(package_file, supports_direct_download: true)
end
desc 'The PyPi Simple Group Index Endpoint' do
detail 'This feature was introduced in GitLab 15.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
# An API entry point but returns an HTML file instead of JSON.
@@ -141,6 +158,13 @@ module API
desc 'The PyPi Simple Group Package Endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
params do
@@ -164,6 +188,13 @@ module API
namespace ':id/packages/pypi' do
desc 'The PyPi package download endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
params do
@@ -185,6 +216,13 @@ module API
desc 'The PyPi Simple Project Index Endpoint' do
detail 'This feature was introduced in GitLab 15.1'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
# An API entry point but returns an HTML file instead of JSON.
@@ -196,6 +234,13 @@ module API
desc 'The PyPi Simple Project Package Endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
end
params do
@@ -211,15 +256,24 @@ module API
desc 'The PyPi Package upload endpoint' do
detail 'This feature was introduced in GitLab 12.10'
+ success code: 201
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' },
+ { code: 422, message: 'Unprocessable Entity' }
+ ]
+ tags %w[pypi_packages]
end
params do
requires :content, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
- requires :name, type: String
- requires :version, type: String
- optional :requires_python, type: String
- optional :md5_digest, type: String
- optional :sha256_digest, type: String, regexp: Gitlab::Regex.sha256_regex
+ requires :name, type: String, documentation: { example: 'my.pypi.package' }
+ requires :version, type: String, documentation: { example: '1.3.7' }
+ optional :requires_python, type: String, documentation: { example: '>=3.7' }
+ optional :md5_digest, type: String, documentation: { example: '900150983cd24fb0d6963f7d28e17f72' }
+ optional :sha256_digest, type: String, regexp: Gitlab::Regex.sha256_regex, documentation: { example: 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' }
end
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
@@ -243,6 +297,17 @@ module API
forbidden!
end
+ desc 'Authorize the PyPi package upload from workhorse' do
+ detail 'This feature was introduced in GitLab 12.10'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pypi_packages]
+ end
+
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
post 'authorize' do
project = project!(action: :read_project)
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index c72f90dfdf3..0e83d086a6e 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -10,13 +10,13 @@ module API
RELEASE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
.merge(tag_name: API::NO_SLASH_URL_PART_REGEX)
- before { authorize! :read_release, user_project }
+ after_validation { authorize! :read_release, user_project }
feature_category :release_orchestration
urgency :low
params do
- requires :id, type: [String, Integer], desc: 'The ID or URL-encoded path of the project'
+ requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
diff --git a/lib/api/rpm_project_packages.rb b/lib/api/rpm_project_packages.rb
index 40b8d022c6c..f02d288982a 100644
--- a/lib/api/rpm_project_packages.rb
+++ b/lib/api/rpm_project_packages.rb
@@ -25,7 +25,16 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/rpm' do
- desc 'Download repository metadata files'
+ desc 'Download repository metadata files' do
+ detail 'This feature was introduced in GitLab 15.7'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
params do
requires :file_name, type: String, desc: 'Repository metadata file name'
end
@@ -40,7 +49,15 @@ module API
present_carrierwave_file!(repository_file.file)
end
- desc 'Download RPM package files'
+ desc 'Download RPM package files' do
+ detail 'This feature was introduced in GitLab 15.7'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
params do
requires :package_file_id, type: Integer, desc: 'RPM package file id'
requires :file_name, type: String, desc: 'RPM package file name'
@@ -56,7 +73,16 @@ module API
not_found!
end
- desc 'Upload a RPM package'
+ desc 'Upload a RPM package' do
+ detail 'This feature was introduced in GitLab 15.7'
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
post do
authorize_create_package!(authorized_user_project)
@@ -64,6 +90,10 @@ module API
bad_request!('File is too large')
end
+ if Packages::Rpm::RepositoryFile.has_oversized_filelists?(project_id: authorized_user_project.id)
+ bad_request!('Repository packages limit exceeded')
+ end
+
track_package_event(
'push_package',
:rpm,
@@ -76,7 +106,15 @@ module API
not_found!
end
- desc 'Authorize package upload from workhorse'
+ desc 'Authorize package upload from workhorse' do
+ detail 'This feature was introduced in GitLab 15.7'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rpm_packages]
+ end
post 'authorize' do
not_found!
end
diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb
index 87cf1f66223..af0ceb1acfc 100644
--- a/lib/api/rubygem_packages.rb
+++ b/lib/api/rubygem_packages.rb
@@ -28,19 +28,27 @@ module API
before do
require_packages_enabled!
authenticate_non_get!
+ end
+
+ after_validation do
not_found! unless Feature.enabled?(:rubygem_packages, user_project)
end
params do
- requires :id, type: String, desc: 'The ID or full path of a project'
+ requires :id, types: [Integer, String], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/rubygems' do
desc 'Download the spec index file' do
detail 'This feature was introduced in GitLab 13.9'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
- requires :file_name, type: String, desc: 'Spec file name'
+ requires :file_name, type: String, desc: 'Spec file name', documentation: { type: 'file' }
end
get ":file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299267
@@ -49,9 +57,14 @@ module API
desc 'Download the gemspec file' do
detail 'This feature was introduced in GitLab 13.9'
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
- requires :file_name, type: String, desc: 'Gemspec file name'
+ requires :file_name, type: String, desc: 'Gemspec file name', documentation: { type: 'file' }
end
get "quick/Marshal.#{MARSHAL_VERSION}/:file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299284
@@ -60,9 +73,16 @@ module API
desc 'Download the .gem package' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
- requires :file_name, type: String, desc: 'Package file name'
+ requires :file_name, type: String, desc: 'Package file name', documentation: { type: 'file' }
end
get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do
authorize_read_package!(user_project)
@@ -80,6 +100,12 @@ module API
namespace 'api/v1' do
desc 'Authorize a gem upload from workhorse' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[rubygem_packages]
end
post 'gems/authorize' do
authorize_workhorse!(
@@ -91,6 +117,13 @@ module API
desc 'Upload a gem' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 201
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[rubygem_packages]
end
params do
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
@@ -133,6 +166,14 @@ module API
desc 'Fetch a list of dependencies' do
detail 'This feature was introduced in GitLab 13.9'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ is_array true
+ tags %w[rubygem_packages]
end
params do
optional :gems, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma delimited gem names'
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 26b7e58bc7a..8b47604fe86 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -66,6 +66,7 @@ module API
requires :eks_secret_access_key, type: String, desc: 'Secret access key for the EKS integration IAM user'
end
optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.'
+ optional :email_confirmation_setting, type: String, values: ApplicationSetting.email_confirmation_settings.keys, desc: "Email confirmation setting, possible values: `off`, `soft`, and `hard`"
optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
optional :gitpod_enabled, type: Boolean, desc: 'Enable Gitpod'
given gitpod_enabled: ->(val) { val } do
@@ -90,7 +91,7 @@ module API
end
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
- values: %w[github bitbucket bitbucket_server gitlab google_code fogbugz git gitlab_project gitea manifest phabricator],
+ values: %w[github bitbucket bitbucket_server gitlab fogbugz git gitlab_project gitea manifest phabricator],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :in_product_marketing_emails_enabled, type: Boolean, desc: 'By default, in-product marketing emails are enabled. To disable these emails, disable this option.'
optional :invisible_captcha_enabled, type: Boolean, desc: 'Enable Invisible Captcha spam detection during signup.'
@@ -100,6 +101,7 @@ module API
optional :max_import_size, type: Integer, desc: 'Maximum import size in MB'
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
optional :max_pages_custom_domains_per_project, type: Integer, desc: 'Maximum number of GitLab Pages custom domains per project'
+ optional :max_terraform_state_size_bytes, type: Integer, desc: "Maximum size in bytes of the Terraform state file. Set this to 0 for unlimited file size."
optional :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.'
optional :password_authentication_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
optional :password_authentication_enabled_for_web, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface'
@@ -139,7 +141,6 @@ module API
requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
end
optional :restricted_visibility_levels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
- optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
given shared_runners_enabled: ->(val) { val } do
@@ -184,6 +185,9 @@ module API
optional :group_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for group runners, in seconds'
optional :project_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for project runners, in seconds'
optional :pipeline_limit_per_project_user_sha, type: Integer, desc: "Maximum number of pipeline creation requests allowed per minute per user and commit. Set to 0 for unlimited requests per minute."
+ optional :jira_connect_application_key, type: String, desc: "Application ID of the OAuth application that should be used to authenticate with the GitLab.com for Jira Cloud app"
+ optional :jira_connect_proxy_url, type: String, desc: "URL of the GitLab instance that should be used as a proxy for the GitLab.com for Jira Cloud app"
+ optional :bulk_import_enabled, type: Boolean, desc: 'Enable migrating GitLab groups and projects by direct transfer'
Gitlab::SSHPublicKey.supported_types.each do |type|
optional :"#{type}_key_restriction",
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index 36698a220bd..104848206a3 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -5,7 +5,7 @@ module API
class Snippets < ::API::Base
include PaginationParams
- feature_category :snippets
+ feature_category :source_code_management
urgency :low
resource :snippets do
diff --git a/lib/api/support/git_access_actor.rb b/lib/api/support/git_access_actor.rb
index 16861a146ae..7a4e6f3e14c 100644
--- a/lib/api/support/git_access_actor.rb
+++ b/lib/api/support/git_access_actor.rb
@@ -16,7 +16,7 @@ module API
def self.from_params(params)
if params[:key_id]
- new(key: Key.find_by_id(params[:key_id]))
+ new(key: Key.auth.find_by_id(params[:key_id]))
elsif params[:user_id]
new(user: UserFinder.new(params[:user_id]).find_by_id)
elsif params[:username]
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index b412a17bc6f..4ddf22c726f 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -129,6 +129,24 @@ module API
end
end
end
+
+ desc "Get a tag's signature" do
+ success code: 200, model: Entities::TagSignature
+ tags %w[tags]
+ failure [
+ { code: 404, message: 'Not found' }
+ ]
+ end
+ params do
+ requires :tag_name, type: String, desc: 'The name of the tag'
+ end
+ get ':id/repository/tags/:tag_name/signature', requirements: TAG_ENDPOINT_REQUIREMENTS, feature_category: :source_code_management do
+ tag = user_project.repository.find_tag(params[:tag_name])
+ not_found! 'Tag' unless tag
+ not_found! 'Signature' unless tag.has_signature?
+
+ present tag, with: Entities::TagSignature
+ end
end
end
end
diff --git a/lib/api/terraform/state.rb b/lib/api/terraform/state.rb
index 577d011ebad..bdc9f975970 100644
--- a/lib/api/terraform/state.rb
+++ b/lib/api/terraform/state.rb
@@ -20,6 +20,8 @@ module API
render_api_error!(e.message, 422)
end
+ STATE_NAME_URI_REQUIREMENTS = { name: API::NO_SLASH_URL_PART_REGEX }.freeze
+
before do
authenticate!
authorize! :read_terraform_state, user_project
@@ -45,7 +47,7 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- namespace ':id/terraform/state/:name' do
+ namespace ':id/terraform/state/:name', requirements: STATE_NAME_URI_REQUIREMENTS do
params do
requires :name, type: String, desc: 'The name of a Terraform state'
optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
@@ -55,6 +57,17 @@ module API
def remote_state_handler
::Terraform::RemoteStateHandler.new(user_project, current_user, name: params[:name], lock_id: params[:ID])
end
+
+ def not_found_for_dots?
+ Feature.disabled?(:allow_dots_on_tf_state_names) && params[:name].include?(".")
+ end
+
+ # Change the state name to behave like before, https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105674
+ # has been introduced. This behavior can be controlled via `allow_dots_on_tf_state_names` FF.
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106861
+ def legacy_state_name!
+ params[:name] = params[:name].split('.').first
+ end
end
desc 'Get a Terraform state by its name' do
@@ -72,6 +85,8 @@ module API
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
get do
+ legacy_state_name! if not_found_for_dots?
+
remote_state_handler.find_with_lock do |state|
no_content! unless state.latest_file && state.latest_file.exists?
@@ -88,17 +103,22 @@ module API
]
failure [
{ code: 403, message: 'Forbidden' },
- { code: 422, message: 'Validation failure' }
+ { code: 422, message: 'Validation failure' },
+ { code: 413, message: 'Request Entity Too Large' }
]
tags %w[terraform_state]
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
post do
authorize! :admin_terraform_state, user_project
+ legacy_state_name! if not_found_for_dots?
data = request.body.read
no_content! if data.empty?
+ max_state_size = Gitlab::CurrentSettings.max_terraform_state_size_bytes
+ file_too_large! if max_state_size > 0 && data.size > max_state_size
+
remote_state_handler.handle_with_lock do |state|
state.update_file!(CarrierWaveStringFile.new(data), version: params[:serial], build: current_authenticated_job)
end
@@ -120,6 +140,7 @@ module API
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
delete do
authorize! :admin_terraform_state, user_project
+ legacy_state_name! if not_found_for_dots?
remote_state_handler.find_with_lock do |state|
::Terraform::States::TriggerDestroyService.new(state, current_user: current_user).execute
@@ -151,6 +172,8 @@ module API
requires :Path, type: String, desc: 'Terraform path'
end
post '/lock' do
+ not_found! if not_found_for_dots?
+
authorize! :admin_terraform_state, user_project
status_code = :ok
@@ -194,6 +217,8 @@ module API
optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
end
delete '/lock' do
+ not_found! if not_found_for_dots?
+
authorize! :admin_terraform_state, user_project
remote_state_handler.unlock!
diff --git a/lib/api/time_tracking_endpoints.rb b/lib/api/time_tracking_endpoints.rb
index b8323304957..dd8ad2cc144 100644
--- a/lib/api/time_tracking_endpoints.rb
+++ b/lib/api/time_tracking_endpoints.rb
@@ -23,14 +23,12 @@ module API
end
def load_issuable
- @issuable ||= begin
- case issuable_name
- when 'issue'
- find_project_issue(params.delete(issuable_key))
- when 'merge_request'
- find_project_merge_request(params.delete(issuable_key))
- end
- end
+ @issuable ||= case issuable_name
+ when 'issue'
+ find_project_issue(params.delete(issuable_key))
+ when 'merge_request'
+ find_project_merge_request(params.delete(issuable_key))
+ end
end
def update_issuable(attrs)
@@ -54,10 +52,18 @@ module API
issuable_collection_name = issuable_name.pluralize
issuable_key = "#{issuable_name}_iid".to_sym
- desc "Set a time estimate for a project #{issuable_name}"
+ desc "Set a time estimate for a #{issuable_name}" do
+ detail " Sets an estimated time of work for this #{issuable_name}."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
- requires :duration, type: String, desc: 'The duration to be parsed'
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}."
+ requires :duration, type: String, desc: 'The duration in human format.', documentation: { example: '3h30m' }
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/time_estimate" do
authorize! admin_issuable_key, load_issuable
@@ -66,9 +72,17 @@ module API
update_issuable(time_estimate: Gitlab::TimeTrackingFormatter.parse(params.delete(:duration)))
end
- desc "Reset the time estimate for a project #{issuable_name}"
+ desc "Reset the time estimate for a project #{issuable_name}" do
+ detail "Resets the estimated time for this #{issuable_name} to 0 seconds."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}."
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_time_estimate" do
authorize! admin_issuable_key, load_issuable
@@ -77,10 +91,18 @@ module API
update_issuable(time_estimate: 0)
end
- desc "Add spent time for a project #{issuable_name}"
+ desc "Add spent time for a #{issuable_name}" do
+ detail "Adds spent time for this #{issuable_name}."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
- requires :duration, type: String, desc: 'The duration to be parsed'
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}."
+ requires :duration, type: String, desc: 'The duration in human format.'
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/add_spent_time" do
authorize! admin_issuable_key, load_issuable
@@ -97,9 +119,17 @@ module API
update_issuable(update_params)
end
- desc "Reset spent time for a project #{issuable_name}"
+ desc "Reset spent time for a #{issuable_name}" do
+ detail "Resets the total spent time for this #{issuable_name} to 0 seconds."
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}"
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_spent_time" do
authorize! admin_issuable_key, load_issuable
@@ -108,9 +138,17 @@ module API
update_issuable(spend_time: { duration: :reset, user_id: current_user.id })
end
- desc "Show time stats for a project #{issuable_name}"
+ desc "Get time tracking stats" do
+ detail "Get time tracking stats"
+ success Entities::IssuableTimeStats
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags [issuable_collection_name]
+ end
params do
- requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
+ requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}"
end
get ":id/#{issuable_collection_name}/:#{issuable_key}/time_stats" do
authorize! read_issuable_key, load_issuable
diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb
index 38ce4bd7f32..b2133dc743a 100644
--- a/lib/api/unleash.rb
+++ b/lib/api/unleash.rb
@@ -63,13 +63,9 @@ module API
cache_context: -> (client) { client.unleash_api_cache_key }
end
- def project
- @project ||= find_project(params[:project_id])
- end
-
def feature_flags_client
strong_memoize(:feature_flags_client) do
- client = Operations::FeatureFlagsClient.find_for_project_and_token(project, unleash_instance_id)
+ client = Operations::FeatureFlagsClient.find_for_project_and_token(params[:project_id], unleash_instance_id)
client.unleash_app_name = unleash_app_name if client
client
end
@@ -86,12 +82,6 @@ module API
def authorize_by_unleash_instance_id!
unauthorized! unless feature_flags_client
end
-
- def feature_flags
- return [] unless unleash_app_name.present?
-
- Operations::FeatureFlag.for_unleash_client(project, unleash_app_name)
- end
end
end
end
diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb
index 9e446aff605..3e2023d769f 100644
--- a/lib/api/usage_data.rb
+++ b/lib/api/usage_data.rb
@@ -12,11 +12,18 @@ module API
forbidden!('Invalid CSRF token is provided') unless verified_request?
end
- desc 'Track usage data events' do
+ desc 'Track usage data event' do
detail 'This feature was introduced in GitLab 13.4.'
+ success code: 200
+ failure [
+ { code: 403, message: 'Invalid CSRF token is provided' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[usage_data]
end
params do
- requires :event, type: String, desc: 'The event name that should be tracked'
+ requires :event, type: String, desc: 'The event name that should be tracked',
+ documentation: { example: 'i_quickactions_page' }
end
post 'increment_counter' do
event_name = params[:event]
@@ -26,8 +33,17 @@ module API
status :ok
end
+ desc 'Track usage data event for the current user' do
+ success code: 200
+ failure [
+ { code: 403, message: 'Invalid CSRF token is provided' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[usage_data]
+ end
params do
- requires :event, type: String, desc: 'The event name that should be tracked'
+ requires :event, type: String, desc: 'The event name that should be tracked',
+ documentation: { example: 'i_quickactions_page' }
end
post 'increment_unique_users', urgency: :low do
event_name = params[:event]
@@ -39,6 +55,13 @@ module API
desc 'Get a list of all metric definitions' do
detail 'This feature was introduced in GitLab 13.11.'
+ success code: 200
+ failure [
+ { code: 403, message: 'Invalid CSRF token is provided' },
+ { code: 404, message: 'Not found' }
+ ]
+ produces ['application/yaml']
+ tags %w[usage_data metrics]
end
get 'metric_definitions', urgency: :low do
content_type 'application/yaml'
diff --git a/lib/api/usage_data_non_sql_metrics.rb b/lib/api/usage_data_non_sql_metrics.rb
index 41f369a43b8..81f96a7958b 100644
--- a/lib/api/usage_data_non_sql_metrics.rb
+++ b/lib/api/usage_data_non_sql_metrics.rb
@@ -14,6 +14,12 @@ module API
desc 'Get Non SQL usage ping metrics' do
detail 'This feature was introduced in GitLab 13.11.'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
end
get 'non_sql_metrics' do
diff --git a/lib/api/usage_data_queries.rb b/lib/api/usage_data_queries.rb
index fe972942111..8e85fca4ba9 100644
--- a/lib/api/usage_data_queries.rb
+++ b/lib/api/usage_data_queries.rb
@@ -14,6 +14,12 @@ module API
desc 'Get raw SQL queries for usage data SQL metrics' do
detail 'This feature was introduced in GitLab 13.11.'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
end
get 'queries' do
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 72c121bca03..d2d45c94291 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -133,7 +133,7 @@ module API
get feature_category: :users, urgency: :low do
authenticated_as_admin! if params[:extern_uid].present? && params[:provider].present?
- unless current_user&.admin?
+ unless current_user&.can_read_all_resources?
params.except!(:created_after, :created_before, :order_by, :sort, :two_factor, :without_projects)
end
@@ -151,7 +151,7 @@ module API
users = UsersFinder.new(current_user, params).execute
users = reorder_users(users)
- entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
+ entity = current_user&.can_read_all_resources? ? Entities::UserWithAdmin : Entities::UserBasic
if entity == Entities::UserWithAdmin
users = users.preload(:identities, :u2f_registrations, :webauthn_registrations, :namespace, :followers, :followees, :user_preference)
@@ -177,7 +177,7 @@ module API
get ":id", feature_category: :users, urgency: :low do
forbidden!('Not authorized!') unless current_user
- unless current_user.admin?
+ unless current_user.can_read_all_resources?
check_rate_limit!(:users_get_by_id,
scope: current_user,
users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
@@ -188,7 +188,7 @@ module API
not_found!('User') unless user && can?(current_user, :read_user, user)
- opts = { with: current_user.admin? ? Entities::UserDetailsWithAdmin : Entities::User, current_user: current_user }
+ opts = { with: current_user.can_read_all_resources? ? Entities::UserDetailsWithAdmin : Entities::User, current_user: current_user }
user, opts = with_custom_attributes(user, opts)
present user, opts
@@ -333,12 +333,12 @@ module API
not_found!('User') unless user
conflict!('Email has already been taken') if params[:email] &&
- User.by_any_email(params[:email].downcase)
- .where.not(id: user.id).exists?
+ User.by_any_email(params[:email].downcase)
+ .where.not(id: user.id).exists?
conflict!('Username has already been taken') if params[:username] &&
- User.by_username(params[:username])
- .where.not(id: user.id).exists?
+ User.by_username(params[:username])
+ .where.not(id: user.id).exists?
user_params = declared_params(include_missing: false)
admin_making_changes_for_another_user = (current_user != user)
@@ -373,7 +373,8 @@ module API
user = User.find_by_id(params[:id])
not_found!('User') unless user
- forbidden!('Two-factor authentication for admins cannot be disabled via the API. Use the Rails console') if user.admin?
+ # We're disabling Cop/UserAdmin because it checks if the given user (not the current user) is an admin.
+ forbidden!('Two-factor authentication for admins cannot be disabled via the API. Use the Rails console') if user.admin? # rubocop:disable Cop/UserAdmin
result = TwoFactor::DestroyService.new(current_user, user: user).execute
@@ -437,6 +438,8 @@ module API
requires :key, type: String, desc: 'The new SSH key'
requires :title, type: String, desc: 'The title of the new SSH key'
optional :expires_at, type: DateTime, desc: 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
+ optional :usage_type, type: String, values: Key.usage_types.keys, default: 'auth_and_signing',
+ desc: 'Scope of usage for the SSH key'
end
# rubocop: disable CodeReuse/ActiveRecord
post ":user_id/keys", feature_category: :authentication_and_authorization do
@@ -1006,7 +1009,8 @@ module API
end
get feature_category: :users, urgency: :low do
entity =
- if current_user.admin?
+ # We're disabling Cop/UserAdmin because it checks if the given user is an admin.
+ if current_user.admin? # rubocop:disable Cop/UserAdmin
Entities::UserWithAdmin
else
Entities::UserPublic
@@ -1050,6 +1054,8 @@ module API
requires :key, type: String, desc: 'The new SSH key'
requires :title, type: String, desc: 'The title of the new SSH key'
optional :expires_at, type: DateTime, desc: 'The expiration date of the SSH key in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'
+ optional :usage_type, type: String, values: Key.usage_types.keys, default: 'auth_and_signing',
+ desc: 'Scope of usage for the SSH key'
end
post "keys", feature_category: :authentication_and_authorization do
key = ::Keys::CreateService.new(current_user, declared_params(include_missing: false)).execute
diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb
index e4a26838746..db71e823b1d 100644
--- a/lib/api/v3/github.rb
+++ b/lib/api/v3/github.rb
@@ -78,13 +78,13 @@ module API
# rubocop: enable CodeReuse/ActiveRecord
def authorized_merge_requests
- MergeRequestsFinder.new(current_user, authorized_only: !current_user.admin?)
+ MergeRequestsFinder.new(current_user, authorized_only: !current_user.can_read_all_resources?)
.execute.with_jira_integration_associations
end
def authorized_merge_requests_for_project(project)
MergeRequestsFinder
- .new(current_user, authorized_only: !current_user.admin?, project_id: project.id)
+ .new(current_user, authorized_only: !current_user.can_read_all_resources?, project_id: project.id)
.execute.with_jira_integration_associations
end
diff --git a/lib/api/validations/validators/array_none_any.rb b/lib/api/validations/validators/array_none_any.rb
index 3732c1f575c..8c064eefbf2 100644
--- a/lib/api/validations/validators/array_none_any.rb
+++ b/lib/api/validations/validators/array_none_any.rb
@@ -8,7 +8,7 @@ module API
value = params[attr_name]
return if value.is_a?(Array) ||
- [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
+ [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],