Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2024-01-16 13:42:19 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2024-01-16 13:42:19 +0300
commit84d1bd786125c1c14a3ba5f63e38a4cc736a9027 (patch)
treef550fa965f507077e20dbb6d61a8269a99ef7107 /lib/api
parent3a105e36e689f7b75482236712f1a47fd5a76814 (diff)
Add latest changes from gitlab-org/gitlab@16-8-stable-eev16.8.0-rc42
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/admin/ci/variables.rb8
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/ci/pipelines.rb2
-rw-r--r--lib/api/ci/runners.rb25
-rw-r--r--lib/api/draft_notes.rb4
-rw-r--r--lib/api/entities/bulk_imports/entity_failure.rb2
-rw-r--r--lib/api/entities/ci/job.rb1
-rw-r--r--lib/api/entities/diff.rb1
-rw-r--r--lib/api/entities/group.rb1
-rw-r--r--lib/api/entities/merge_request_basic.rb5
-rw-r--r--lib/api/entities/ml/mlflow/model_version.rb6
-rw-r--r--lib/api/entities/ml/mlflow/search_experiments.rb14
-rw-r--r--lib/api/entities/pages/deployments.rb14
-rw-r--r--lib/api/entities/pages/project_settings.rb14
-rw-r--r--lib/api/entities/user_preferences.rb2
-rw-r--r--lib/api/group_variables.rb30
-rw-r--r--lib/api/groups.rb6
-rw-r--r--lib/api/helpers.rb26
-rw-r--r--lib/api/helpers/integrations_helpers.rb314
-rw-r--r--lib/api/helpers/kubernetes/agent_helpers.rb3
-rw-r--r--lib/api/helpers/packages/maven.rb4
-rw-r--r--lib/api/helpers/user_preferences_helpers.rb17
-rw-r--r--lib/api/internal/kubernetes.rb1
-rw-r--r--lib/api/maven_packages.rb8
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/api/merge_request_approvals.rb3
-rw-r--r--lib/api/ml/mlflow/api_helpers.rb22
-rw-r--r--lib/api/ml/mlflow/experiments.rb36
-rw-r--r--lib/api/ml/mlflow/model_versions.rb5
-rw-r--r--lib/api/ml/mlflow/registered_models.rb8
-rw-r--r--lib/api/namespaces.rb3
-rw-r--r--lib/api/npm_project_packages.rb18
-rw-r--r--lib/api/pages.rb19
-rw-r--r--lib/api/terraform/modules/v1/namespace_packages.rb (renamed from lib/api/terraform/modules/v1/packages.rb)65
-rw-r--r--lib/api/terraform/modules/v1/project_packages.rb215
-rw-r--r--lib/api/users.rb8
36 files changed, 458 insertions, 456 deletions
diff --git a/lib/api/admin/ci/variables.rb b/lib/api/admin/ci/variables.rb
index 3277acc1b52..a443f7f3476 100644
--- a/lib/api/admin/ci/variables.rb
+++ b/lib/api/admin/ci/variables.rb
@@ -54,6 +54,10 @@ module API
type: String,
desc: 'The key of the variable. Max 255 characters'
+ optional :description,
+ type: String,
+ desc: 'The description of the variable'
+
requires :value,
type: String,
desc: 'The value of a variable'
@@ -98,6 +102,10 @@ module API
type: String,
desc: 'The key of a variable'
+ optional :description,
+ type: String,
+ desc: 'The description of the variable'
+
optional :value,
type: String,
desc: 'The value of a variable'
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 97e09795f49..94b433193dd 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -330,7 +330,7 @@ module API
mount ::API::Suggestions
mount ::API::SystemHooks
mount ::API::Tags
- mount ::API::Terraform::Modules::V1::Packages
+ mount ::API::Terraform::Modules::V1::NamespacePackages
mount ::API::Terraform::Modules::V1::ProjectPackages
mount ::API::Terraform::State
mount ::API::Terraform::StateVersion
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index b5123ab49dc..f369fc5e183 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -183,7 +183,7 @@ module API
.new(current_user: current_user, pipeline: pipeline, params: params)
.execute
- builds = builds.with_preloads
+ builds = builds.with_preloads.preload(:metadata) # rubocop:disable CodeReuse/ActiveRecord -- preload job.archived?
present paginate(builds), with: Entities::Ci::Job
end
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 17bee275c51..300c30faf4a 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -94,6 +94,14 @@ module API
forbidden!("No access granted") unless can?(current_user, :read_builds, runner)
end
+
+ def preload_job_associations(jobs)
+ jobs.preload( # rubocop: disable CodeReuse/ActiveRecord -- this preload is tightly related to the endpoint
+ :user,
+ { pipeline: { project: [:route, { namespace: :route }] } },
+ { project: [:route, { namespace: :route }] }
+ )
+ end
end
resource :runners do
@@ -217,25 +225,24 @@ module API
end
params do
requires :id, type: Integer, desc: 'The ID of a runner'
+ optional :system_id, type: String, desc: 'System ID associated with the runner manager'
optional :status, type: String, desc: 'Status of the job', values: ::Ci::Build::AVAILABLE_STATUSES
optional :order_by, type: String, desc: 'Order by `id`', values: ::Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by `asc` or `desc` order. ' \
- 'Specify `order_by` as well, including for `id`'
+ 'Specify `order_by` as well, including for `id`'
+ optional :cursor, type: String, desc: 'Cursor for obtaining the next set of records'
use :pagination
end
get ':id/jobs' do
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
+ # Optimize query when filtering by runner managers by not asking for count
+ paginator_params = params[:pagination] == :keyset || params[:system_id].blank? ? {} : { without_count: true }
+
jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
- jobs = jobs.preload( # rubocop: disable CodeReuse/ActiveRecord
- [
- :user,
- { pipeline: { project: [:route, { namespace: :route }] } },
- { project: [:route, { namespace: :route }] }
- ]
- )
- jobs = paginate(jobs)
+ jobs = preload_job_associations(jobs)
+ jobs = paginate_with_strategies(jobs, paginator_params: paginator_params)
jobs.each(&:commit) # batch loads all commits in the page
present jobs, with: Entities::Ci::JobBasicWithProject
diff --git a/lib/api/draft_notes.rb b/lib/api/draft_notes.rb
index 6fadc68233d..3d046ec4a9b 100644
--- a/lib/api/draft_notes.rb
+++ b/lib/api/draft_notes.rb
@@ -22,7 +22,9 @@ module API
end
def delete_draft_note(draft_note)
- ::DraftNotes::DestroyService.new(user_project, current_user).execute(draft_note)
+ ::DraftNotes::DestroyService
+ .new(merge_request(params: params), current_user)
+ .execute(draft_note)
end
def publish_draft_note(params:)
diff --git a/lib/api/entities/bulk_imports/entity_failure.rb b/lib/api/entities/bulk_imports/entity_failure.rb
index 08708a7c961..4771e6cb894 100644
--- a/lib/api/entities/bulk_imports/entity_failure.rb
+++ b/lib/api/entities/bulk_imports/entity_failure.rb
@@ -6,7 +6,7 @@ module API
class EntityFailure < Grape::Entity
expose :relation, documentation: { type: 'string', example: 'label' }
expose :exception_message, documentation: { type: 'string', example: 'error message' } do |failure|
- ::Projects::ImportErrorFilter.filter_message(failure.exception_message.truncate(72))
+ ::Projects::ImportErrorFilter.filter_message(failure.exception_message).truncate(255)
end
expose :exception_class, documentation: { type: 'string', example: 'Exception' }
expose :correlation_id_value, documentation: { type: 'string', example: 'dfcf583058ed4508e4c7c617bd7f0edd' }
diff --git a/lib/api/entities/ci/job.rb b/lib/api/entities/ci/job.rb
index d9e6b7eed75..2f748d28abf 100644
--- a/lib/api/entities/ci/job.rb
+++ b/lib/api/entities/ci/job.rb
@@ -12,6 +12,7 @@ module API
expose :runner, with: ::API::Entities::Ci::Runner
expose :artifacts_expire_at,
documentation: { type: 'dateTime', example: '2016-01-19T09:05:50.355Z' }
+ expose :archived?, as: :archived, documentation: { type: 'boolean', example: false }
expose(
:tag_list,
diff --git a/lib/api/entities/diff.rb b/lib/api/entities/diff.rb
index cc53736a5b1..e1e6ce26263 100644
--- a/lib/api/entities/diff.rb
+++ b/lib/api/entities/diff.rb
@@ -16,6 +16,7 @@ module API
expose :new_file?, as: :new_file, documentation: { type: 'boolean' }
expose :renamed_file?, as: :renamed_file, documentation: { type: 'boolean' }
expose :deleted_file?, as: :deleted_file, documentation: { type: 'boolean' }
+ expose :generated?, as: :generated_file, documentation: { type: 'boolean' }
end
end
end
diff --git a/lib/api/entities/group.rb b/lib/api/entities/group.rb
index 1a1765c2e0a..14491c2396a 100644
--- a/lib/api/entities/group.rb
+++ b/lib/api/entities/group.rb
@@ -23,6 +23,7 @@ module API
expose :full_name, :full_path
expose :created_at
expose :parent_id
+ expose :organization_id
expose :shared_runners_setting
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb
index 56519e2bf08..39bb54bfc5a 100644
--- a/lib/api/entities/merge_request_basic.rb
+++ b/lib/api/entities/merge_request_basic.rb
@@ -97,6 +97,11 @@ module API
expose :squash
expose :squash_on_merge?, as: :squash_on_merge
expose :task_completion_status
+
+ # #cannot_be_merged? is generally indicative of conflicts, and is set via
+ # MergeRequests::MergeabilityCheckService. However, it can also indicate
+ # that either #has_no_commits? or #branch_missing? are true.
+ #
expose :cannot_be_merged?, as: :has_conflicts
expose :mergeable_discussions_state?, as: :blocking_discussions_resolved
diff --git a/lib/api/entities/ml/mlflow/model_version.rb b/lib/api/entities/ml/mlflow/model_version.rb
index 10fdf3822a5..d57def4e1f2 100644
--- a/lib/api/entities/ml/mlflow/model_version.rb
+++ b/lib/api/entities/ml/mlflow/model_version.rb
@@ -18,7 +18,7 @@ module API
expose :run_id
expose :status
expose :status_message
- expose :metadata
+ expose :metadata, as: :tags, using: KeyValue
expose :run_link
expose :aliases, documentation: { is_array: true, type: String }
@@ -68,10 +68,6 @@ module API
""
end
- def metadata
- []
- end
-
def run_link
""
end
diff --git a/lib/api/entities/ml/mlflow/search_experiments.rb b/lib/api/entities/ml/mlflow/search_experiments.rb
new file mode 100644
index 00000000000..9673cb1b6fd
--- /dev/null
+++ b/lib/api/entities/ml/mlflow/search_experiments.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ml
+ module Mlflow
+ class SearchExperiments < Grape::Entity # rubocop:disable Search/NamespacedClass -- Not related to search
+ expose :experiments, with: Experiment
+ expose :next_page_token
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/pages/deployments.rb b/lib/api/entities/pages/deployments.rb
new file mode 100644
index 00000000000..143fbe93344
--- /dev/null
+++ b/lib/api/entities/pages/deployments.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Pages
+ class Deployments < Grape::Entity
+ expose :created_at
+ expose :url
+ expose :path_prefix
+ expose :root_directory
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/pages/project_settings.rb b/lib/api/entities/pages/project_settings.rb
new file mode 100644
index 00000000000..81a48fe8bd3
--- /dev/null
+++ b/lib/api/entities/pages/project_settings.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Pages
+ class ProjectSettings < Grape::Entity
+ expose :url
+ expose :deployments, using: "API::Entities::Pages::Deployments"
+ expose :unique_domain_enabled?, as: :is_unique_domain_enabled
+ expose :force_https?, as: :force_https
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/user_preferences.rb b/lib/api/entities/user_preferences.rb
index e04ddd52f29..3824e3f1b37 100644
--- a/lib/api/entities/user_preferences.rb
+++ b/lib/api/entities/user_preferences.rb
@@ -8,5 +8,3 @@ module API
end
end
end
-
-API::Entities::UserPreferences.prepend_mod_with('API::Entities::UserPreferences', with_descendants: true)
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index f320fa06394..94702c36c85 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -106,11 +106,31 @@ module API
declared_params(include_missing: false)
)
- variable = ::Ci::ChangeVariableService.new(
- container: user_group,
- current_user: current_user,
- params: { action: :update, variable_params: filtered_params }
- ).execute
+ # If the 'filter' parameter is provided, the user is updating a scoped variable
+ # and we need to use `find_variable` to make sure we update the correct one.
+ # However, this would result in an error response in case the user attempts to
+ # update a scoped variable without providing a filter. This error response is
+ # technically correct, because updating a scoped variable without specifying
+ # the targeted scope causes non-deterministic behavior. But this endpoint was
+ # originally introduced without the ability to specify a filter, and returning
+ # an error in these cases now would be considered a breaking change.
+ # Thus we only use the new/correct code if the user provided a filter.
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136475
+ if params.key?('filter')
+ variable = find_variable(user_group, params)
+
+ variable = ::Ci::ChangeVariableService.new(
+ container: user_group,
+ current_user: current_user,
+ params: { action: :update, variable: variable, variable_params: filtered_params }
+ ).execute
+ else
+ variable = ::Ci::ChangeVariableService.new(
+ container: user_group,
+ current_user: current_user,
+ params: { action: :update, variable_params: filtered_params }
+ ).execute
+ end
if variable.valid?
present variable, with: Entities::Ci::Variable
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 1ff64cd2ffd..7b755a76f29 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -213,11 +213,15 @@ module API
requires :name, type: String, desc: 'The name of the group'
requires :path, type: String, desc: 'The path of the group'
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
+ optional :organization_id, type: Integer, desc: 'The organization id for the group'
use :optional_params
end
post feature_category: :groups_and_projects, urgency: :low do
- parent_group = find_group!(params[:parent_id]) if params[:parent_id].present?
+ organization = find_organization!(params[:organization_id]) if params[:organization_id].present?
+ authorize! :create_group, organization if organization
+
+ parent_group = find_group!(params[:parent_id], organization: organization) if params[:parent_id].present?
if parent_group
authorize! :create_subgroup, parent_group
else
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index f5dcbc07704..a59734d643d 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -184,8 +184,7 @@ module API
return true unless job_token_authentication?
return true unless route_authentication_setting[:job_token_scope] == :project
- ::Feature.enabled?(:ci_job_token_scope, project) &&
- current_authenticated_job.project == project
+ current_authenticated_job.project == project
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -212,18 +211,25 @@ module API
not_found!('Pipeline')
end
+ def find_organization!(id)
+ organization = Organizations::Organization.find_by_id(id)
+ check_organization_access(organization)
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
- def find_group(id)
+ def find_group(id, organization: nil)
+ collection = organization.present? ? Group.in_organization(organization) : Group.all
+
if id.to_s =~ INTEGER_ID_REGEX
- Group.find_by(id: id)
+ collection.find_by(id: id)
else
- Group.find_by_full_path(id)
+ collection.find_by_full_path(id)
end
end
# rubocop: enable CodeReuse/ActiveRecord
- def find_group!(id)
- group = find_group(id)
+ def find_group!(id, organization: nil)
+ group = find_group(id, organization: organization)
check_group_access(group)
end
@@ -836,6 +842,12 @@ module API
@sudo_identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
end
+ def check_organization_access(organization)
+ return organization if can?(current_user, :read_organization, organization)
+
+ not_found!('Organization')
+ end
+
def secret_token
Gitlab::Shell.secret_token
end
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index dd3009ff1d7..b450718a7d0 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -7,35 +7,6 @@ module API
# The data structures inside this model are returned using class methods,
# allowing EE to extend them where necessary.
module IntegrationsHelpers
- def self.chat_notification_settings
- [
- {
- required: true,
- name: :webhook,
- type: String,
- desc: 'The chat webhook'
- },
- {
- required: false,
- name: :username,
- type: String,
- desc: 'The chat username'
- },
- {
- required: false,
- name: :channel,
- type: String,
- desc: 'The default chat channel'
- },
- {
- required: false,
- name: :branches_to_be_notified,
- type: String,
- desc: 'Branches for which notifications are to be sent'
- }
- ].freeze
- end
-
def self.chat_notification_flags
[
{
@@ -129,58 +100,8 @@ module API
'apple-app-store' => ::Integrations::AppleAppStore.api_fields,
'asana' => ::Integrations::Asana.api_fields,
'assembla' => ::Integrations::Assembla.api_fields,
- 'bamboo' => [
- {
- required: true,
- name: :bamboo_url,
- type: String,
- desc: 'Bamboo root URL like https://bamboo.example.com'
- },
- {
- required: false,
- name: :enable_ssl_verification,
- type: ::Grape::API::Boolean,
- desc: 'Enable SSL verification'
- },
- {
- required: true,
- name: :build_key,
- type: String,
- desc: 'Bamboo build plan key like'
- },
- {
- required: true,
- name: :username,
- type: String,
- desc: 'A user with API access, if applicable'
- },
- {
- required: true,
- name: :password,
- type: String,
- desc: 'Password of the user'
- }
- ],
- 'bugzilla' => [
- {
- required: true,
- name: :new_issue_url,
- type: String,
- desc: 'New issue URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'Issues URL'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'Project URL'
- }
- ],
+ 'bamboo' => ::Integrations::Bamboo.api_fields,
+ 'bugzilla' => ::Integrations::Bugzilla.api_fields,
'buildkite' => [
{
required: true,
@@ -201,54 +122,9 @@ module API
desc: 'DEPRECATED: This parameter has no effect since SSL verification will always be enabled'
}
],
- 'campfire' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'Campfire token'
- },
- {
- required: false,
- name: :subdomain,
- type: String,
- desc: 'Campfire subdomain'
- },
- {
- required: false,
- name: :room,
- type: String,
- desc: 'Campfire room'
- }
- ],
- 'confluence' => [
- {
- required: true,
- name: :confluence_url,
- type: String,
- desc: 'The URL of the Confluence Cloud Workspace hosted on atlassian.net'
- }
- ],
- 'custom-issue-tracker' => [
- {
- required: true,
- name: :new_issue_url,
- type: String,
- desc: 'New issue URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'Issues URL'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'Project URL'
- }
- ],
+ 'campfire' => ::Integrations::Campfire.api_fields,
+ 'confluence' => ::Integrations::Confluence.api_fields,
+ 'custom-issue-tracker' => ::Integrations::CustomIssueTracker.api_fields,
'datadog' => [
{
required: true,
@@ -293,19 +169,9 @@ module API
desc: 'Custom tags in Datadog. Specify one tag per line in the format: "key:value\nkey2:value2"'
}
],
+ 'diffblue-cover' => ::Integrations::DiffblueCover.api_fields,
'discord' => [
- {
- required: true,
- name: :webhook,
- type: String,
- desc: 'Discord webhook. For example, https://discord.com/api/webhooks/…'
- },
- {
- required: false,
- name: :branches_to_be_notified,
- type: String,
- desc: 'Branches for which notifications are to be sent'
- },
+ ::Integrations::Discord.api_fields,
chat_notification_flags,
chat_notification_channels
].flatten,
@@ -355,40 +221,8 @@ module API
desc: 'Branches for which notifications are to be sent'
}
],
- 'external-wiki' => [
- {
- required: true,
- name: :external_wiki_url,
- type: String,
- desc: 'The URL of the external wiki'
- }
- ],
- 'google-play' => [
- {
- required: true,
- name: :package_name,
- type: String,
- desc: 'The package name of the app in Google Play'
- },
- {
- required: true,
- name: :service_account_key,
- type: String,
- desc: 'The Google Play service account key'
- },
- {
- required: true,
- name: :service_account_key_file_name,
- type: String,
- desc: 'The filename of the Google Play service account key'
- },
- {
- required: false,
- name: :google_play_protected_refs,
- type: ::Grape::API::Boolean,
- desc: 'Only enable for protected refs'
- }
- ],
+ 'external-wiki' => ::Integrations::ExternalWiki.api_fields,
+ 'google-play' => ::Integrations::GooglePlay.api_fields,
'hangouts-chat' => [
{
required: true,
@@ -403,32 +237,7 @@ module API
desc: 'Branches for which notifications are to be sent'
}
].flatten,
- 'harbor' => [
- {
- required: true,
- name: :url,
- type: String,
- desc: 'The base URL to the Harbor instance which is being linked to this GitLab project. For example, https://demo.goharbor.io.'
- },
- {
- required: true,
- name: :project_name,
- type: String,
- desc: 'The Project name to the Harbor instance. For example, testproject.'
- },
- {
- required: true,
- name: :username,
- type: String,
- desc: 'The username created from Harbor interface.'
- },
- {
- required: true,
- name: :password,
- type: String,
- desc: 'The password of the user.'
- }
- ],
+ 'harbor' => ::Integrations::Harbor.api_fields,
'irker' => [
{
required: true,
@@ -555,14 +364,7 @@ module API
desc: 'Enable comments inside Jira issues on each GitLab event (commit / merge request)'
}
],
- 'mattermost-slash-commands' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'The Mattermost token'
- }
- ],
+ 'mattermost-slash-commands' => ::Integrations::MattermostSlashCommands.api_fields,
'slack-slash-commands' => [
{
required: true,
@@ -697,77 +499,12 @@ module API
desc: 'The sound of the notification'
}
],
- 'redmine' => [
- {
- required: true,
- name: :new_issue_url,
- type: String,
- desc: 'The new issue URL'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'The project URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'The issues URL'
- }
- ],
- 'ewm' => [
- {
- required: true,
- name: :new_issue_url,
- type: String,
- desc: 'New Issue URL'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'Project URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'Issues URL'
- }
- ],
- 'youtrack' => [
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'The project URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'The issues URL'
- }
- ],
- 'clickup' => [
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'The project URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'The issues URL'
- }
- ],
+ 'redmine' => ::Integrations::Redmine.api_fields,
+ 'ewm' => ::Integrations::Ewm.api_fields,
+ 'youtrack' => ::Integrations::Youtrack.api_fields,
+ 'clickup' => ::Integrations::Clickup.api_fields,
'slack' => [
- chat_notification_settings,
- chat_notification_flags,
+ ::Integrations::Slack.api_fields,
chat_notification_channels
].flatten,
'microsoft-teams' => [
@@ -786,8 +523,7 @@ module API
chat_notification_flags
].flatten,
'mattermost' => [
- chat_notification_settings,
- chat_notification_flags,
+ ::Integrations::Mattermost.api_fields,
chat_notification_channels
].flatten,
'teamcity' => [
@@ -879,20 +615,7 @@ module API
desc: 'The product ID of ZenTao project'
}
],
- 'squash-tm' => [
- {
- required: true,
- name: :url,
- type: String,
- desc: 'The Squash TM webhook URL'
- },
- {
- required: false,
- name: :token,
- type: String,
- desc: 'The secret token'
- }
- ]
+ 'squash-tm' => ::Integrations::SquashTm.api_fields
}
end
@@ -909,6 +632,7 @@ module API
::Integrations::Confluence,
::Integrations::CustomIssueTracker,
::Integrations::Datadog,
+ ::Integrations::DiffblueCover,
::Integrations::Discord,
::Integrations::DroneCi,
::Integrations::EmailsOnPush,
diff --git a/lib/api/helpers/kubernetes/agent_helpers.rb b/lib/api/helpers/kubernetes/agent_helpers.rb
index eca26c023cf..18f47fa9955 100644
--- a/lib/api/helpers/kubernetes/agent_helpers.rb
+++ b/lib/api/helpers/kubernetes/agent_helpers.rb
@@ -40,7 +40,6 @@ module API
def increment_unique_events
events = params[:unique_counters]&.slice(
- :agent_users_using_ci_tunnel,
:k8s_api_proxy_requests_unique_agents_via_ci_access,
:k8s_api_proxy_requests_unique_agents_via_user_access,
:k8s_api_proxy_requests_unique_agents_via_pat_access,
@@ -60,6 +59,8 @@ module API
)
return if event_lists.blank?
+ event_lists[:agent_users_using_ci_tunnel] = event_lists.values.flatten
+
users, projects = load_users_and_projects(event_lists)
event_lists.each do |event_name, events|
track_events_for(event_name, events, users, projects)
diff --git a/lib/api/helpers/packages/maven.rb b/lib/api/helpers/packages/maven.rb
index 6c50f4c00a1..f7c8da3e641 100644
--- a/lib/api/helpers/packages/maven.rb
+++ b/lib/api/helpers/packages/maven.rb
@@ -19,11 +19,11 @@ module API
documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
- def extract_format(file_name)
+ def extract_format(file_name, skip_fips_check: false)
name, _, format = file_name.rpartition('.')
if %w[md5 sha1].include?(format)
- unprocessable_entity! if Gitlab::FIPS.enabled? && format == 'md5'
+ unprocessable_entity! if !skip_fips_check && Gitlab::FIPS.enabled? && format == 'md5'
[name, format]
else
diff --git a/lib/api/helpers/user_preferences_helpers.rb b/lib/api/helpers/user_preferences_helpers.rb
deleted file mode 100644
index 846ad354156..00000000000
--- a/lib/api/helpers/user_preferences_helpers.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Helpers
- module UserPreferencesHelpers
- extend ActiveSupport::Concern
- extend Grape::API::Helpers
-
- def update_user_namespace_settings(attrs)
- # This method will be redefined in EE.
- attrs
- end
- end
- end
-end
-
-API::Helpers::UserPreferencesHelpers.prepend_mod_with('API::Helpers::UserPreferencesHelpers')
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index d3a4d94f8ca..286192f8093 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -127,7 +127,6 @@ module API
end
optional :unique_counters, type: Hash do
- optional :agent_users_using_ci_tunnel, type: Array[Integer], desc: 'An array of user ids that have interacted with CI Tunnel'
optional :k8s_api_proxy_requests_unique_users_via_ci_access, type: Array[Integer], desc: 'An array of users that have interacted with the CI tunnel via `ci_access`'
optional :k8s_api_proxy_requests_unique_agents_via_ci_access, type: Array[Integer], desc: 'An array of agents that have interacted with the CI tunnel via `ci_access`'
optional :k8s_api_proxy_requests_unique_users_via_user_access, type: Array[Integer], desc: 'An array of users that have interacted with the CI tunnel via `user_access`'
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index 14c3fccee32..e969935383a 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -260,7 +260,13 @@ module API
authorize_upload!
bad_request!('File is too large') if user_project.actual_limits.exceeded?(:maven_max_file_size, params[:file].size)
- file_name, format = extract_format(params[:file_name])
+ # In FIPS mode, we've already told Workhorse not to generate a
+ # MD5 checksum via UploadHashFunctions, and the FIPS check above
+ # ensures that Workhorse obeys that. However, Gradle will attempt to issue a PUT request
+ # with the MD5 checksum, and the publish step will fail if this endpoint returns a
+ # 422 (https://github.com/gradle/gradle/blob/v8.5.0/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/publisher/AbstractMavenPublisher.java#L240),
+ # so we need to skip the second FIPS check here.
+ file_name, format = extract_format(params[:file_name], skip_fips_check: true)
::Gitlab::Database::LoadBalancing::Session.current.use_primary do
result = ::Packages::Maven::FindOrCreatePackageService
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 56a15c41e1c..908733d4aa1 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -176,7 +176,7 @@ module API
source = find_source(source_type, params[:id])
member = source_members(source).find_by!(user_id: params[:user_id])
- check_rate_limit!(:member_delete, scope: [source, current_user])
+ check_rate_limit!(:members_delete, scope: [source, current_user])
destroy_conditionally!(member) do
::Members::DestroyService.new(current_user).execute(member, skip_subresources: params[:skip_subresources], unassign_issuables: params[:unassign_issuables])
diff --git a/lib/api/merge_request_approvals.rb b/lib/api/merge_request_approvals.rb
index d0c9400039a..23d7dafdc0a 100644
--- a/lib/api/merge_request_approvals.rb
+++ b/lib/api/merge_request_approvals.rb
@@ -104,7 +104,8 @@ module API
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)
+ unauthorized! unless current_user.can?(:reset_merge_request_approvals, merge_request) &&
+ !merge_request.merged?
merge_request.approvals.delete_all
diff --git a/lib/api/ml/mlflow/api_helpers.rb b/lib/api/ml/mlflow/api_helpers.rb
index 66d79753110..c81c66ca798 100644
--- a/lib/api/ml/mlflow/api_helpers.rb
+++ b/lib/api/ml/mlflow/api_helpers.rb
@@ -5,6 +5,7 @@ module API
module Mlflow
module ApiHelpers
OUTER_QUOTES_REGEXP = /^("|')|("|')?$/
+ GITLAB_TAG_PREFIX = 'gitlab.'
def check_api_read!
not_found! unless can?(current_user, :read_model_experiments, user_project)
@@ -113,6 +114,27 @@ module API
{ name: filter }
end
+ def gitlab_tags
+ return unless params[:tags].present?
+
+ tags = params[:tags]
+ gitlab_params = {}
+
+ tags.each do |tag|
+ key, value = tag.values_at(:key, :value)
+
+ gitlab_params[key.delete_prefix(GITLAB_TAG_PREFIX)] = value if key&.starts_with?(GITLAB_TAG_PREFIX)
+ end
+
+ gitlab_params
+ end
+
+ def custom_version
+ return unless gitlab_tags
+
+ gitlab_tags['version']
+ end
+
def find_experiment!(iid, name)
experiment_repository.by_iid_or_name(iid: iid, name: name) || resource_not_found!
end
diff --git a/lib/api/ml/mlflow/experiments.rb b/lib/api/ml/mlflow/experiments.rb
index 1a501291941..511922782e8 100644
--- a/lib/api/ml/mlflow/experiments.rb
+++ b/lib/api/ml/mlflow/experiments.rb
@@ -47,6 +47,42 @@ module API
present response, with: Entities::Ml::Mlflow::ListExperiment
end
+ desc 'Search experiments' do
+ success Entities::Ml::Mlflow::ListExperiment
+ detail 'https://www.mlflow.org/docs/latest/rest-api.html#list-experiments'
+ end
+ params do
+ optional :max_results,
+ type: Integer,
+ desc: 'Maximum number of experiments to fetch in a page. Default is 200, maximum is 1000.',
+ default: 200
+ optional :order_by,
+ type: String,
+ desc: 'Order criteria. Can be by a column of the experiment (created_at, name).',
+ default: 'created_at DESC'
+ optional :page_token,
+ type: String,
+ desc: 'Token for pagination'
+ optional :filter,
+ type: String,
+ desc: 'This parameter is ignored'
+ end
+ post 'search', urgency: :low do
+ max_results = [params[:max_results], 1000].min
+
+ finder_params = model_order_params(params)
+
+ finder = ::Projects::Ml::ExperimentFinder.new(user_project, finder_params)
+ paginator = finder.execute.keyset_paginate(cursor: params[:page_token], per_page: max_results)
+
+ result = {
+ experiments: paginator.records,
+ next_page_token: paginator.cursor_for_next_page
+ }
+
+ present result, with: Entities::Ml::Mlflow::SearchExperiments
+ end
+
desc 'Create experiment' do
success Entities::Ml::Mlflow::NewExperiment
detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#create-experiment'
diff --git a/lib/api/ml/mlflow/model_versions.rb b/lib/api/ml/mlflow/model_versions.rb
index 4b211cf540c..53ba4ff36f1 100644
--- a/lib/api/ml/mlflow/model_versions.rb
+++ b/lib/api/ml/mlflow/model_versions.rb
@@ -27,13 +27,16 @@ module API
desc: 'Register model under this name This field is required.'
optional :description, type: String,
desc: 'Optional description for model version.'
+ optional :tags, type: Array, desc: 'Additional metadata for a model version.'
end
post 'create', urgency: :low do
present ::Ml::CreateModelVersionService.new(
model,
{
model_name: params[:name],
- description: params[:description]
+ description: params[:description],
+ metadata: params[:tags],
+ version: custom_version
}
).execute,
with: Entities::Ml::Mlflow::ModelVersion,
diff --git a/lib/api/ml/mlflow/registered_models.rb b/lib/api/ml/mlflow/registered_models.rb
index a68a2767a74..3f4996a94c0 100644
--- a/lib/api/ml/mlflow/registered_models.rb
+++ b/lib/api/ml/mlflow/registered_models.rb
@@ -31,13 +31,17 @@ module API
optional :tags, type: Array, desc: 'Additional metadata for registered model.'
end
post 'create', urgency: :low do
- present ::Ml::CreateModelService.new(
+ model = ::Ml::CreateModelService.new(
user_project,
params[:name],
current_user,
params[:description],
params[:tags]
- ).execute,
+ ).execute
+
+ resource_already_exists! unless model.persisted?
+
+ present model,
with: Entities::Ml::Mlflow::RegisteredModel,
root: :registered_model
rescue ActiveRecord::RecordInvalid
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index 750dc7fc2a1..17425c288fc 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -34,6 +34,7 @@ module API
params do
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'
+ optional :top_level_only, type: Boolean, default: false, desc: 'Only include top level namespaces'
use :pagination
use :optional_list_params_ee
@@ -43,6 +44,8 @@ module API
namespaces = current_user.admin ? Namespace.all : current_user.namespaces(owned_only: owned_only)
+ namespaces = namespaces.top_most if params[:top_level_only]
+
namespaces = namespaces.without_project_namespaces.include_route
namespaces = namespaces.include_gitlab_subscription_with_hosted_plan if Gitlab.ee?
diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb
index e1d0455b1e2..c6c99944ca0 100644
--- a/lib/api/npm_project_packages.rb
+++ b/lib/api/npm_project_packages.rb
@@ -1,6 +1,13 @@
# frozen_string_literal: true
module API
class NpmProjectPackages < ::API::Base
+ ERROR_REASON_TO_HTTP_STATUS_MAPPTING = {
+ ::Packages::Npm::CreatePackageService::ERROR_REASON_INVALID_PARAMETER => 400,
+ ::Packages::Npm::CreatePackageService::ERROR_REASON_PACKAGE_LEASE_TAKEN => 400,
+ ::Packages::Npm::CreatePackageService::ERROR_REASON_PACKAGE_EXISTS => 403,
+ ::Packages::Npm::CreatePackageService::ERROR_REASON_PACKAGE_PROTECTED => 403
+ }.freeze
+
helpers ::API::Helpers::Packages::Npm
feature_category :package_registry
@@ -14,6 +21,10 @@ module API
def endpoint_scope
:project
end
+
+ def error_reason_to_http_status(reason)
+ ERROR_REASON_TO_HTTP_STATUS_MAPPTING.fetch(reason, 400)
+ end
end
params do
@@ -74,12 +85,13 @@ module API
else
authorize_create_package!(project)
- created_package = ::Packages::Npm::CreatePackageService
+ service_response = ::Packages::Npm::CreatePackageService
.new(project, current_user, params.merge(build: current_authenticated_job)).execute
- if created_package[:status] == :error
- render_structured_api_error!({ message: created_package[:message], error: created_package[:message] }, created_package[:http_status])
+ if service_response.error?
+ render_structured_api_error!({ message: service_response.message, error: service_response.message }, error_reason_to_http_status(service_response.reason))
else
+ created_package = service_response[:package]
enqueue_sync_metadata_cache_worker(project, created_package.name)
track_package_event('push_package', :npm, category: 'API::NpmPackages', project: project, namespace: project.namespace)
created_package
diff --git a/lib/api/pages.rb b/lib/api/pages.rb
index 0cedf7d975f..30e126b34cb 100644
--- a/lib/api/pages.rb
+++ b/lib/api/pages.rb
@@ -6,7 +6,6 @@ module API
before do
require_pages_config_enabled!
- authenticated_with_can_read_all_resources!
end
params do
@@ -24,12 +23,30 @@ module API
tags %w[pages]
end
delete ':id/pages' do
+ authenticated_with_can_read_all_resources!
authorize! :remove_pages, user_project
::Pages::DeleteService.new(user_project, current_user).execute
no_content!
end
+
+ desc 'Get pages settings' do
+ detail 'Get pages URL and other settings. This feature was introduced in Gitlab 16.8'
+ success code: 200
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[pages]
+ end
+ get ':id/pages' do
+ authorize! :read_pages, user_project
+
+ break not_found! unless user_project.pages_enabled?
+
+ present ::Pages::ProjectSettings.new(user_project), with: Entities::Pages::ProjectSettings
+ end
end
end
end
diff --git a/lib/api/terraform/modules/v1/packages.rb b/lib/api/terraform/modules/v1/namespace_packages.rb
index 9e82a849c98..1999fc42aba 100644
--- a/lib/api/terraform/modules/v1/packages.rb
+++ b/lib/api/terraform/modules/v1/namespace_packages.rb
@@ -4,7 +4,7 @@ module API
module Terraform
module Modules
module V1
- class Packages < ::API::Base
+ class NamespacePackages < ::API::Base
include ::API::Helpers::Authentication
helpers ::API::Helpers::PackagesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
@@ -29,8 +29,10 @@ module API
end
helpers do
+ include ::Gitlab::Utils::StrongMemoize
+
params :module_name do
- requires :module_name, type: String, desc: "", regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :module_name, type: String, desc: '', regexp: API::NO_SLASH_URL_PART_REGEX
requires :module_system, type: String, regexp: API::NO_SLASH_URL_PART_REGEX
end
@@ -39,10 +41,9 @@ module API
end
def module_namespace
- strong_memoize(:module_namespace) do
- find_namespace(params[:module_namespace])
- end
+ find_namespace(params[:module_namespace])
end
+ strong_memoize_attr :module_namespace
def finder_params
{
@@ -55,26 +56,23 @@ module API
end
def packages
- strong_memoize(:packages) do
- ::Packages::GroupPackagesFinder.new(
- current_user,
- module_namespace,
- finder_params
- ).execute
- end
+ ::Packages::GroupPackagesFinder.new(
+ current_user,
+ module_namespace,
+ finder_params
+ ).execute
end
+ strong_memoize_attr :packages
def package
- strong_memoize(:package) do
- packages.first
- end
+ packages.first
end
+ strong_memoize_attr :package
def package_file
- strong_memoize(:package_file) do
- package.installable_package_files.first
- end
+ package.installable_package_files.first
end
+ strong_memoize_attr :package_file
end
params do
@@ -82,7 +80,8 @@ module API
includes :module_name
end
- namespace 'packages/terraform/modules/v1/:module_namespace/:module_name/:module_system', requirements: TERRAFORM_MODULE_REQUIREMENTS do
+ namespace 'packages/terraform/modules/v1/:module_namespace/:module_name/:module_system',
+ requirements: TERRAFORM_MODULE_REQUIREMENTS do
authenticate_with do |accept|
accept.token_types(:personal_access_token, :deploy_token, :job_token)
.sent_through(:http_bearer_token)
@@ -118,7 +117,9 @@ module API
get 'download' do
latest_version = packages.order_version.last&.version
- render_api_error!({ error: "No version found for #{params[:module_name]} module" }, :not_found) if latest_version.nil?
+ if latest_version.nil?
+ render_api_error!({ error: "No version found for #{params[:module_name]} module" }, :not_found)
+ end
download_path = api_v4_packages_terraform_modules_v1_module_version_download_path(
{
@@ -145,7 +146,9 @@ module API
get do
latest_package = packages.order_version.last
- render_api_error!({ error: "No version found for #{params[:module_name]} module" }, :not_found) if latest_package&.version.nil?
+ if latest_package&.version.nil?
+ render_api_error!({ error: "No version found for #{params[:module_name]} module" }, :not_found)
+ end
presenter = ::Terraform::ModuleVersionPresenter.new(latest_package, params[:module_system])
present presenter, with: ::API::Entities::Terraform::ModuleVersion
@@ -181,13 +184,18 @@ module API
jwt_token = Gitlab::TerraformRegistryToken.from_token(token_from_namespace_inheritable).encoded
end
- header 'X-Terraform-Get', module_file_path.sub(%r{module_version/file$}, "#{params[:module_version]}/file?token=#{jwt_token}&archive=tgz")
+ header 'X-Terraform-Get',
+ module_file_path.sub(
+ %r{module_version/file$},
+ "#{params[:module_version]}/file?token=#{jwt_token}&archive=tgz"
+ )
status :no_content
end
namespace 'file' do
authenticate_with do |accept|
- accept.token_types(:deploy_token_from_jwt, :job_token_from_jwt, :personal_access_token_from_jwt).sent_through(:token_param)
+ accept.token_types(:deploy_token_from_jwt, :job_token_from_jwt, :personal_access_token_from_jwt)
+ .sent_through(:token_param)
end
desc 'Download specific version of a module' do
@@ -200,9 +208,14 @@ module API
tags %w[terraform_registry]
end
get do
- track_package_event('pull_package', :terraform_module, project: package.project, namespace: module_namespace)
-
- present_carrierwave_file!(package_file.file)
+ track_package_event(
+ 'pull_package',
+ :terraform_module,
+ project: package.project,
+ namespace: module_namespace
+ )
+
+ present_package_file!(package_file)
end
end
diff --git a/lib/api/terraform/modules/v1/project_packages.rb b/lib/api/terraform/modules/v1/project_packages.rb
index 07dfddefefc..c0a84c7b36c 100644
--- a/lib/api/terraform/modules/v1/project_packages.rb
+++ b/lib/api/terraform/modules/v1/project_packages.rb
@@ -16,87 +16,174 @@ module API
require_packages_enabled!
end
- params do
- requires :id, type: String, desc: 'The ID or full path of a project'
- requires :module_name, type: String, desc: "", regexp: API::NO_SLASH_URL_PART_REGEX
- requires :module_system, type: String, regexp: API::NO_SLASH_URL_PART_REGEX
- requires :module_version, type: String, desc: 'Module version', regexp: Gitlab::Regex.semver_regex
- end
+ helpers do
+ params :terraform_get do
+ optional 'terraform-get', type: String, values: %w[1], desc: 'Terraform get redirection flag'
+ end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- namespace ':id/packages/terraform/modules/:module_name/:module_system/*module_version/file' do
- authenticate_with do |accept|
- accept.token_types(:deploy_token).sent_through(:http_deploy_token_header)
- accept.token_types(:job_token).sent_through(:http_job_token_header)
- accept.token_types(:personal_access_token).sent_through(:http_private_token_header)
+ def present_package_file
+ authorize_read_package!(authorized_user_project)
+
+ if declared_params[:'terraform-get'] == '1'
+ header 'X-Terraform-Get', "#{request.url.split('?').first}?archive=tgz"
+ return no_content!
end
- desc 'Workhorse authorize Terraform Module package file' do
- detail 'This feature was introduced in GitLab 13.11'
- success code: 200
- failure [
- { code: 403, message: 'Forbidden' }
- ]
- tags %w[terraform_registry]
+ package = ::Packages::TerraformModule::PackagesFinder
+ .new(authorized_user_project, finder_params)
+ .execute
+ .first
+
+ not_found! unless package
+
+ track_package_event('pull_package', :terraform_module, project: authorized_user_project,
+ namespace: authorized_user_project.namespace)
+
+ present_package_file!(package.installable_package_files.first)
+ end
+
+ def finder_params
+ { package_name: package_name }.tap do |finder_params|
+ finder_params[:package_version] = params[:module_version] if params.key?(:module_version)
end
+ end
+
+ def package_name
+ "#{params[:module_name]}/#{params[:module_system]}"
+ end
+ end
- put 'authorize' do
- authorize_workhorse!(
- subject: authorized_user_project,
- maximum_size: authorized_user_project.actual_limits.terraform_module_max_file_size
- )
+ params do
+ requires :id, types: [String, Integer], allow_blank: false, desc: 'The ID or full path of a project'
+ with(type: String, allow_blank: false, regexp: API::NO_SLASH_URL_PART_REGEX) do
+ requires :module_name, desc: 'Module name', documentation: { example: 'infra-registry' }
+ requires :module_system, desc: 'Module system', documentation: { example: 'aws' }
+ end
+ end
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ namespace ':id/packages/terraform/modules/:module_name/:module_system' do
+ authenticate_with do |accept|
+ accept.token_types(
+ :personal_access_token_with_username,
+ :deploy_token_with_username,
+ :job_token_with_username
+ ).sent_through(:http_basic_auth)
end
- desc 'Upload Terraform Module package file' do
- detail 'This feature was introduced in GitLab 13.11'
- success code: 201
+ desc 'Download the latest version of a module' do
+ detail 'This feature was introduced in GitLab 16.7'
+ success code: 204
failure [
- { code: 400, message: 'Invalid file' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not found' }
]
- consumes %w[multipart/form-data]
tags %w[terraform_registry]
end
-
params do
- requires :file, type: ::API::Validations::Types::WorkhorseFile,
- desc: 'The package file to be published (generated by Multipart middleware)',
- documentation: { type: 'file' }
+ use :terraform_get
+ end
+ get do
+ present_package_file
end
- put do
- authorize_upload!(authorized_user_project)
-
- bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(
- :terraform_module_max_file_size, params[:file].size)
-
- create_package_file_params = {
- module_name: params['module_name'],
- module_system: params['module_system'],
- module_version: params['module_version'],
- file: params['file'],
- build: current_authenticated_job
- }
-
- result = ::Packages::TerraformModule::CreatePackageService
- .new(authorized_user_project, current_user, create_package_file_params)
- .execute
-
- render_api_error!(result[:message], result[:http_status]) if result[:status] == :error
-
- track_package_event('push_package', :terraform_module, project: authorized_user_project,
- namespace: authorized_user_project.namespace)
-
- created!
- rescue ObjectStorage::RemoteStoreError => e
- Gitlab::ErrorTracking.track_exception(
- e,
- extra: { file_name: params[:file_name], project_id: authorized_user_project.id }
- )
-
- forbidden!
+ params do
+ requires :module_version, type: String, allow_blank: false, desc: 'Module version',
+ regexp: Gitlab::Regex.semver_regex
+ end
+ namespace '*module_version' do
+ desc 'Download a specific version of a module' do
+ detail 'This feature was introduced in GitLab 16.7'
+ success code: 204
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ tags %w[terraform_registry]
+ end
+ params do
+ use :terraform_get
+ end
+ get format: false do
+ present_package_file
+ end
+
+ namespace :file do
+ authenticate_with do |accept|
+ accept.token_types(:deploy_token).sent_through(:http_deploy_token_header)
+ accept.token_types(:job_token).sent_through(:http_job_token_header)
+ accept.token_types(:personal_access_token).sent_through(:http_private_token_header)
+ end
+
+ desc 'Workhorse authorize Terraform Module package file' do
+ detail 'This feature was introduced in GitLab 13.11'
+ success code: 200
+ failure [
+ { code: 403, message: 'Forbidden' }
+ ]
+ tags %w[terraform_registry]
+ end
+
+ put :authorize do
+ authorize_workhorse!(
+ subject: authorized_user_project,
+ maximum_size: authorized_user_project.actual_limits.terraform_module_max_file_size
+ )
+ end
+
+ desc 'Upload Terraform Module package file' do
+ detail 'This feature was introduced in GitLab 13.11'
+ success code: 201
+ failure [
+ { code: 400, message: 'Invalid file' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ consumes %w[multipart/form-data]
+ tags %w[terraform_registry]
+ end
+
+ params do
+ requires :file, type: ::API::Validations::Types::WorkhorseFile,
+ desc: 'The package file to be published (generated by Multipart middleware)',
+ documentation: { type: 'file' }
+ end
+
+ put do
+ authorize_upload!(authorized_user_project)
+
+ bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(
+ :terraform_module_max_file_size, params[:file].size
+ )
+
+ create_package_file_params = {
+ module_name: params['module_name'],
+ module_system: params['module_system'],
+ module_version: params['module_version'],
+ file: params['file'],
+ build: current_authenticated_job
+ }
+
+ result = ::Packages::TerraformModule::CreatePackageService
+ .new(authorized_user_project, current_user, create_package_file_params)
+ .execute
+
+ render_api_error!(result.message, result.reason) if result.error?
+
+ track_package_event('push_package', :terraform_module, project: authorized_user_project,
+ namespace: authorized_user_project.namespace)
+
+ created!
+ rescue ObjectStorage::RemoteStoreError => e
+ Gitlab::ErrorTracking.track_exception(e,
+ extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
+
+ forbidden!
+ end
+ end
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 38fa247055e..8b54fb84dd2 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -1077,8 +1077,6 @@ module API
end
end
- helpers Helpers::UserPreferencesHelpers
-
desc "Get the currently authenticated user's SSH keys" do
success Entities::SSHKey
end
@@ -1269,9 +1267,7 @@ module API
optional :view_diffs_file_by_file, type: Boolean, desc: 'Flag indicating the user sees only one file diff per page'
optional :show_whitespace_in_diffs, type: Boolean, desc: 'Flag indicating the user sees whitespace changes in diffs'
optional :pass_user_identities_to_ci_jwt, type: Boolean, desc: 'Flag indicating the user passes their external identities to a CI job as part of a JSON web token.'
- optional :code_suggestions, type: Boolean, desc: 'Flag indicating the user allows code suggestions.' \
- 'Argument is experimental and can be removed in the future without notice.'
- at_least_one_of :view_diffs_file_by_file, :show_whitespace_in_diffs, :pass_user_identities_to_ci_jwt, :code_suggestions
+ at_least_one_of :view_diffs_file_by_file, :show_whitespace_in_diffs, :pass_user_identities_to_ci_jwt
end
put "preferences", feature_category: :user_profile, urgency: :high do
authenticate!
@@ -1280,8 +1276,6 @@ module API
attrs = declared_params(include_missing: false)
- attrs = update_user_namespace_settings(attrs)
-
render_api_error!('400 Bad Request', 400) unless attrs
service = ::UserPreferences::UpdateService.new(current_user, attrs).execute