debian-mirror-gitlab/lib/api/projects.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

948 lines
38 KiB
Ruby
Raw Normal View History

2018-12-05 23:21:45 +05:30
# frozen_string_literal: true
2014-09-02 18:07:02 +05:30
module API
2021-01-03 14:25:43 +05:30
class Projects < ::API::Base
2017-08-17 22:00:37 +05:30
include PaginationParams
2018-03-27 19:54:05 +05:30
include Helpers::CustomAttributes
2019-09-04 21:01:54 +05:30
helpers Helpers::ProjectsHelpers
2017-08-17 22:00:37 +05:30
before { authenticate_non_get! }
2021-01-29 00:20:46 +05:30
feature_category :projects, ['/projects/:id/custom_attributes', '/projects/:id/custom_attributes/:key']
2021-04-29 21:17:54 +05:30
PROJECT_ATTACHMENT_SIZE_EXEMPT = 1.gigabyte
2018-11-18 11:00:15 +05:30
helpers do
# EE::API::Projects would override this method
def apply_filters(projects)
projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
projects = projects.with_statistics if params[:statistics]
2020-07-28 23:09:34 +05:30
projects = projects.joins(:statistics) if params[:order_by].include?('project_statistics') # rubocop: disable CodeReuse/ActiveRecord
2022-06-21 17:19:12 +05:30
projects = projects.created_by(current_user).imported.with_import_state if params[:imported]
2018-11-18 11:00:15 +05:30
2019-03-02 22:35:43 +05:30
lang = params[:with_programming_language]
projects = projects.with_programming_language(lang) if lang
2018-11-18 11:00:15 +05:30
projects
end
def verify_update_project_attrs!(project, attrs)
2020-04-08 14:13:33 +05:30
attrs.delete(:repository_storage) unless can?(current_user, :change_repository_storage, project)
2018-11-18 11:00:15 +05:30
end
2020-01-01 13:55:28 +05:30
2020-07-28 23:09:34 +05:30
def verify_project_filters!(attrs)
attrs.delete(:repository_storage) unless can?(current_user, :use_project_statistics_filters)
end
def verify_statistics_order_by_projects!
return unless Helpers::ProjectsHelpers::STATISTICS_SORT_PARAMS.include?(params[:order_by])
params[:order_by] = if can?(current_user, :use_project_statistics_filters)
"project_statistics.#{params[:order_by]}"
else
route.params['order_by'][:default]
end
end
2021-10-27 15:23:28 +05:30
def support_order_by_similarity!(attrs)
return unless params[:order_by] == 'similarity'
if order_by_similarity?(allow_unauthorized: false)
# Limit to projects the current user is a member of.
# Do not include all public projects because it
# could cause long running queries
attrs[:non_public] = true
attrs[:sort] = params['order_by']
else
params[:order_by] = route.params['order_by'][:default]
end
end
2020-01-01 13:55:28 +05:30
def delete_project(user_project)
destroy_conditionally!(user_project) do
::Projects::DestroyService.new(user_project, current_user, {}).async_execute
end
accepted!
end
2021-04-29 21:17:54 +05:30
def exempt_from_global_attachment_size?(user_project)
list = ::Gitlab::RackAttack::UserAllowlist.new(ENV['GITLAB_UPLOAD_API_ALLOWLIST'])
list.include?(user_project.id)
end
# Temporarily introduced for upload API: https://gitlab.com/gitlab-org/gitlab/-/issues/325788
def project_attachment_size(user_project)
return PROJECT_ATTACHMENT_SIZE_EXEMPT if exempt_from_global_attachment_size?(user_project)
2022-07-16 23:28:13 +05:30
return user_project.max_attachment_size if Feature.enabled?(:enforce_max_attachment_size_upload_api, user_project)
2021-04-29 21:17:54 +05:30
PROJECT_ATTACHMENT_SIZE_EXEMPT
end
# This is to help determine which projects to use in https://gitlab.com/gitlab-org/gitlab/-/issues/325788
def log_if_upload_exceed_max_size(user_project, file)
return if file.size <= user_project.max_attachment_size
if file.size > user_project.max_attachment_size
allowed = exempt_from_global_attachment_size?(user_project)
Gitlab::AppLogger.info({ message: "File exceeds maximum size", file_bytes: file.size, project_id: user_project.id, project_path: user_project.full_path, upload_allowed: allowed })
end
end
2018-11-18 11:00:15 +05:30
end
2017-08-17 22:00:37 +05:30
helpers do
params :statistics_params do
optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
end
2014-09-02 18:07:02 +05:30
2017-09-10 17:25:29 +05:30
params :collection_params do
use :sort_params
use :filter_params
use :pagination
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
optional :simple, type: Boolean, default: false,
desc: 'Return only the ID, URL, name, and path of each project'
end
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
params :sort_params do
2020-07-28 23:09:34 +05:30
optional :order_by, type: String,
2021-10-27 15:23:28 +05:30
values: %w[id name path created_at updated_at last_activity_at similarity] + Helpers::ProjectsHelpers::STATISTICS_SORT_PARAMS,
default: 'created_at', desc: "Return projects ordered by field. #{Helpers::ProjectsHelpers::STATISTICS_SORT_PARAMS.join(', ')} are only available to admins. Similarity is available when searching and is limited to projects the user has access to."
2017-09-10 17:25:29 +05:30
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return projects sorted in ascending and descending order'
end
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
params :filter_params do
2018-11-18 11:00:15 +05:30
optional :archived, type: Boolean, desc: 'Limit by archived status'
2017-09-10 17:25:29 +05:30
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of projects matching the search criteria'
2020-04-22 19:07:51 +05:30
optional :search_namespaces, type: Boolean, desc: "Include ancestor namespaces when matching search criteria"
2017-09-10 17:25:29 +05:30
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :starred, type: Boolean, default: false, desc: 'Limit by starred status'
2022-06-21 17:19:12 +05:30
optional :imported, type: Boolean, default: false, desc: 'Limit by imported by authenticated user'
2017-09-10 17:25:29 +05:30
optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of'
optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
2019-03-02 22:35:43 +05:30
optional :with_programming_language, type: String, desc: 'Limit to repositories which use the given programming language'
2018-11-18 11:00:15 +05:30
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
2019-12-26 22:10:19 +05:30
optional :id_after, type: Integer, desc: 'Limit results to projects with IDs greater than the specified ID'
optional :id_before, type: Integer, desc: 'Limit results to projects with IDs less than the specified ID'
2020-04-22 19:07:51 +05:30
optional :last_activity_after, type: DateTime, desc: 'Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
optional :last_activity_before, type: DateTime, desc: 'Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
2020-07-28 23:09:34 +05:30
optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
2021-06-08 01:23:25 +05:30
optional :topic, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of topics. Limit results to projects having all topics'
2022-08-13 15:12:31 +05:30
optional :topic_id, type: Integer, desc: 'Limit results to projects with the assigned topic given by the topic ID'
2018-11-18 11:00:15 +05:30
2019-09-04 21:01:54 +05:30
use :optional_filter_params_ee
2017-09-10 17:25:29 +05:30
end
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
params :create_params do
optional :namespace_id, type: Integer, desc: 'Namespace ID for the new project. Default to the user namespace.'
optional :import_url, type: String, desc: 'URL from which the project is imported'
2019-12-04 20:38:33 +05:30
optional :template_name, type: String, desc: "Name of template from which to create project"
2019-12-26 22:10:19 +05:30
optional :template_project_id, type: Integer, desc: "Project ID of template from which to create project"
mutually_exclusive :import_url, :template_name, :template_project_id
2017-09-10 17:25:29 +05:30
end
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
def load_projects
2021-10-27 15:23:28 +05:30
project_params = project_finder_params
support_order_by_similarity!(project_params)
verify_project_filters!(project_params)
ProjectsFinder.new(current_user: current_user, params: project_params).execute
2018-03-17 18:26:18 +05:30
end
2021-11-11 11:23:49 +05:30
def present_project(project, options = {})
options[:with].preload_resource(project) if options[:with].respond_to?(:preload_resource)
present project, options
end
2018-03-17 18:26:18 +05:30
def present_projects(projects, options = {})
2020-07-28 23:09:34 +05:30
verify_statistics_order_by_projects!
2021-10-27 15:23:28 +05:30
projects = reorder_projects(projects) unless order_by_similarity?(allow_unauthorized: false)
2018-11-18 11:00:15 +05:30
projects = apply_filters(projects)
2017-08-17 22:00:37 +05:30
2020-05-24 23:13:21 +05:30
records, options = paginate_with_strategies(projects, options[:request_scope]) do |projects|
2020-03-13 15:44:24 +05:30
projects, options = with_custom_attributes(projects, options)
options = options.reverse_merge(
with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
statistics: params[:statistics],
current_user: current_user,
license: false
)
options[:with] = Entities::BasicProjectDetails if params[:simple]
[options[:with].prepare_relation(projects, options), options]
end
2017-09-10 17:25:29 +05:30
2020-03-13 15:44:24 +05:30
present records, options
2014-09-02 18:07:02 +05:30
end
2018-10-15 14:42:47 +05:30
2021-04-17 20:07:23 +05:30
def present_groups(groups)
options = {
with: Entities::PublicGroupDetails,
current_user: current_user
}
groups, options = with_custom_attributes(groups, options)
present paginate(groups), options
end
2018-10-15 14:42:47 +05:30
def translate_params_for_compatibility(params)
params[:builds_enabled] = params.delete(:jobs_enabled) if params.key?(:jobs_enabled)
params
end
2022-07-16 23:28:13 +05:30
def add_import_params(params)
params[:import_type] = 'git' if params[:import_url]&.present?
params
end
2017-09-10 17:25:29 +05:30
end
2014-09-02 18:07:02 +05:30
2019-03-02 22:35:43 +05:30
resource :users, requirements: API::USER_REQUIREMENTS do
2017-09-10 17:25:29 +05:30
desc 'Get a user projects' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::BasicProjectDetails
failure [{ code: 404, message: '404 User Not Found' }]
tags %w[projects]
is_array true
2017-09-10 17:25:29 +05:30
end
params do
requires :user_id, type: String, desc: 'The ID or username of the user'
use :collection_params
use :statistics_params
2018-03-27 19:54:05 +05:30
use :with_custom_attributes
2017-09-10 17:25:29 +05:30
end
2022-07-16 23:28:13 +05:30
get ":user_id/projects", feature_category: :projects, urgency: :low do
2017-09-10 17:25:29 +05:30
user = find_user(params[:user_id])
not_found!('User') unless user
params[:user] = user
2018-03-17 18:26:18 +05:30
present_projects load_projects
2017-09-10 17:25:29 +05:30
end
2019-10-12 21:52:04 +05:30
desc 'Get projects starred by a user' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::BasicProjectDetails
failure [{ code: 404, message: '404 User Not Found' }]
tags %w[projects]
is_array true
2019-10-12 21:52:04 +05:30
end
params do
requires :user_id, type: String, desc: 'The ID or username of the user'
use :collection_params
use :statistics_params
end
2022-07-16 23:28:13 +05:30
get ":user_id/starred_projects", feature_category: :projects, urgency: :low do
2019-10-12 21:52:04 +05:30
user = find_user(params[:user_id])
not_found!('User') unless user
starred_projects = StarredProjectsFinder.new(user, params: project_finder_params, current_user: current_user).execute
present_projects starred_projects
end
2017-09-10 17:25:29 +05:30
end
resource :projects do
2018-03-17 18:26:18 +05:30
include CustomAttributesEndpoints
2017-08-17 22:00:37 +05:30
desc 'Get a list of visible projects for authenticated user' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::BasicProjectDetails
tags %w[projects]
is_array true
2017-08-17 22:00:37 +05:30
end
params do
use :collection_params
use :statistics_params
2018-03-27 19:54:05 +05:30
use :with_custom_attributes
2017-08-17 22:00:37 +05:30
end
2022-06-21 17:19:12 +05:30
# TODO: Set higher urgency https://gitlab.com/gitlab-org/gitlab/-/issues/211495
get feature_category: :projects, urgency: :low do
2018-03-17 18:26:18 +05:30
present_projects load_projects
2014-09-02 18:07:02 +05:30
end
2017-08-17 22:00:37 +05:30
desc 'Create new project' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::Project
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 400, message: 'Bad request' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
params do
2023-03-04 22:38:38 +05:30
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' }
2017-08-17 22:00:37 +05:30
at_least_one_of :name, :path
2019-12-04 20:38:33 +05:30
use :optional_create_project_params
2017-08-17 22:00:37 +05:30
use :create_params
end
2022-07-16 23:28:13 +05:30
post urgency: :low do
2021-04-29 21:17:54 +05:30
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/21139')
2017-08-17 22:00:37 +05:30
attrs = declared_params(include_missing: false)
2018-10-15 14:42:47 +05:30
attrs = translate_params_for_compatibility(attrs)
2022-07-16 23:28:13 +05:30
attrs = add_import_params(attrs)
2019-09-30 21:07:59 +05:30
filter_attributes_using_license!(attrs)
2021-12-11 22:18:48 +05:30
2022-07-16 23:28:13 +05:30
validate_git_import_url!(params[:import_url])
2021-12-11 22:18:48 +05:30
2017-08-17 22:00:37 +05:30
project = ::Projects::CreateService.new(current_user, attrs).execute
if project.saved?
2021-11-11 11:23:49 +05:30
present_project project, with: Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project),
current_user: current_user
2014-09-02 18:07:02 +05:30
else
2017-08-17 22:00:37 +05:30
if project.errors[:limit_reached].present?
error!(project.errors[:limit_reached], 403)
2014-09-02 18:07:02 +05:30
end
2018-03-17 18:26:18 +05:30
2022-07-16 23:28:13 +05:30
forbidden! if project.errors[:import_source_disabled].present?
2017-08-17 22:00:37 +05:30
render_validation_error!(project)
2014-09-02 18:07:02 +05:30
end
end
2017-08-17 22:00:37 +05:30
desc 'Create new project for a specified user. Only available to admin users.' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::Project
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 400, message: 'Bad request' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
params do
2023-03-04 22:38:38 +05:30
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' }
2018-05-09 12:01:36 +05:30
use :optional_project_params
2019-12-26 22:10:19 +05:30
use :optional_create_project_params
2017-08-17 22:00:37 +05:30
use :create_params
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
post "user/:user_id", feature_category: :projects do
2021-04-29 21:17:54 +05:30
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/21139')
2014-09-02 18:07:02 +05:30
authenticated_as_admin!
2017-08-17 22:00:37 +05:30
user = User.find_by(id: params.delete(:user_id))
not_found!('User') unless user
attrs = declared_params(include_missing: false)
2018-10-15 14:42:47 +05:30
attrs = translate_params_for_compatibility(attrs)
2022-07-16 23:28:13 +05:30
attrs = add_import_params(attrs)
2019-09-30 21:07:59 +05:30
filter_attributes_using_license!(attrs)
2021-12-11 22:18:48 +05:30
validate_git_import_url!(params[:import_url])
2017-08-17 22:00:37 +05:30
project = ::Projects::CreateService.new(user, attrs).execute
if project.saved?
2021-11-11 11:23:49 +05:30
present_project project, with: Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project),
current_user: current_user
2014-09-02 18:07:02 +05:30
else
2022-07-16 23:28:13 +05:30
forbidden! if project.errors[:import_source_disabled].present?
2017-08-17 22:00:37 +05:30
render_validation_error!(project)
2015-04-26 12:48:37 +05:30
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2017-08-17 22:00:37 +05:30
end
2015-04-26 12:48:37 +05:30
2017-08-17 22:00:37 +05:30
params do
2023-01-13 00:05:48 +05:30
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
2017-08-17 22:00:37 +05:30
end
2019-02-15 15:39:39 +05:30
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
2017-08-17 22:00:37 +05:30
desc 'Get a single project' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::ProjectWithAccess
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
params do
use :statistics_params
2018-03-27 19:54:05 +05:30
use :with_custom_attributes
2018-12-13 13:39:08 +05:30
optional :license, type: Boolean, default: false,
desc: 'Include project license data'
2017-08-17 22:00:37 +05:30
end
2022-06-21 17:19:12 +05:30
# TODO: Set higher urgency https://gitlab.com/gitlab-org/gitlab/-/issues/357622
2022-07-16 23:28:13 +05:30
get ":id", feature_category: :projects, urgency: :low do
2018-03-27 19:54:05 +05:30
options = {
with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
current_user: current_user,
user_can_admin_project: can?(current_user, :admin_project, user_project),
2018-12-13 13:39:08 +05:30
statistics: params[:statistics],
license: params[:license]
2018-03-27 19:54:05 +05:30
}
project, options = with_custom_attributes(user_project, options)
2021-11-11 11:23:49 +05:30
present_project project, options
2017-08-17 22:00:37 +05:30
end
desc 'Fork new project for the current user or provided namespace.' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::Project
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 409, message: 'Conflict' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
params do
2023-03-04 22:38:38 +05:30
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' }
2021-03-11 19:13:27 +05:30
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork'
2022-11-25 23:54:43 +05:30
optional :mr_default_target_self, type: Boolean, desc: 'Merge requests of this forked project targets itself by default'
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post ':id/fork', feature_category: :source_code_management do
2021-04-29 21:17:54 +05:30
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20759')
2018-03-17 18:26:18 +05:30
2020-04-08 14:13:33 +05:30
not_found! unless can?(current_user, :fork_project, user_project)
2016-09-29 09:46:39 +05:30
2020-04-08 14:13:33 +05:30
fork_params = declared_params(include_missing: false)
2016-09-29 09:46:39 +05:30
2020-04-08 14:13:33 +05:30
fork_params[:namespace] =
if fork_params[:namespace_id].present?
find_namespace!(fork_params[:namespace_id])
elsif fork_params[:namespace_path].present?
find_namespace_by_path!(fork_params[:namespace_path])
elsif fork_params[:namespace].present?
find_namespace!(fork_params[:namespace])
2016-09-29 09:46:39 +05:30
end
2020-04-08 14:13:33 +05:30
service = ::Projects::ForkService.new(user_project, current_user, fork_params)
not_found!('Target Namespace') unless service.valid_fork_target?
forked_project = service.execute
2016-09-29 09:46:39 +05:30
2017-08-17 22:00:37 +05:30
if forked_project.errors.any?
conflict!(forked_project.errors.messages)
2015-04-26 12:48:37 +05:30
else
2021-11-11 11:23:49 +05:30
present_project forked_project, {
with: Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, forked_project),
current_user: current_user
}
2016-04-02 18:10:28 +05:30
end
2015-04-26 12:48:37 +05:30
end
2018-03-17 18:26:18 +05:30
desc 'List forks of this project' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::Project
tags %w[projects]
is_array true
2018-03-17 18:26:18 +05:30
end
params do
use :collection_params
2018-03-27 19:54:05 +05:30
use :with_custom_attributes
2018-03-17 18:26:18 +05:30
end
2021-12-11 22:18:48 +05:30
get ':id/forks', feature_category: :source_code_management, urgency: :low do
2018-03-17 18:26:18 +05:30
forks = ForkProjectsFinder.new(user_project, params: project_finder_params, current_user: current_user).execute
2020-05-24 23:13:21 +05:30
present_projects forks, request_scope: user_project
2018-03-17 18:26:18 +05:30
end
2023-03-04 22:38:38 +05:30
desc 'Check pages access of this project' do
success code: 200
failure [
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
end
2023-01-13 00:05:48 +05:30
get ':id/pages_access', urgency: :low, feature_category: :pages do
2018-12-05 23:21:45 +05:30
authorize! :read_pages_content, user_project unless user_project.public_pages?
status 200
end
2017-08-17 22:00:37 +05:30
desc 'Update an existing project' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::Project
failure [
{ code: 400, message: 'Bad request' },
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
params do
2023-03-04 22:38:38 +05:30
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' }
2017-08-17 22:00:37 +05:30
2018-05-09 12:01:36 +05:30
use :optional_project_params
2021-01-03 14:25:43 +05:30
use :optional_update_params
2018-11-18 11:00:15 +05:30
2019-07-07 11:18:12 +05:30
at_least_one_of(*Helpers::ProjectsHelpers.update_params_at_least_one_of)
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
put ':id', feature_category: :projects do
2015-04-26 12:48:37 +05:30
authorize_admin_project
2017-08-17 22:00:37 +05:30
attrs = declared_params(include_missing: false)
2015-04-26 12:48:37 +05:30
authorize! :rename_project, user_project if attrs[:name].present?
2021-10-29 20:43:33 +05:30
authorize! :change_visibility_level, user_project if user_project.visibility_attribute_present?(attrs)
2015-04-26 12:48:37 +05:30
2018-10-15 14:42:47 +05:30
attrs = translate_params_for_compatibility(attrs)
2022-07-16 23:28:13 +05:30
attrs = add_import_params(attrs)
2019-09-30 21:07:59 +05:30
filter_attributes_using_license!(attrs)
2023-03-17 16:20:25 +05:30
filter_attributes_under_feature_flag!(attrs, user_project)
2018-11-18 11:00:15 +05:30
verify_update_project_attrs!(user_project, attrs)
2022-10-11 01:57:18 +05:30
user_project.remove_avatar! if attrs.key?(:avatar) && attrs[:avatar].nil?
2017-08-17 22:00:37 +05:30
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if result[:status] == :success
2021-11-11 11:23:49 +05:30
present_project user_project, with: Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, user_project),
current_user: current_user
2017-08-17 22:00:37 +05:30
else
render_validation_error!(user_project)
2014-09-02 18:07:02 +05:30
end
end
2017-08-17 22:00:37 +05:30
desc 'Archive a project' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::Project
failure [
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post ':id/archive', feature_category: :projects do
2016-06-02 11:05:42 +05:30
authorize!(:archive_project, user_project)
2018-11-18 11:00:15 +05:30
::Projects::UpdateService.new(user_project, current_user, archived: true).execute
2016-06-02 11:05:42 +05:30
2021-11-11 11:23:49 +05:30
present_project user_project, with: Entities::Project, current_user: current_user
2016-06-02 11:05:42 +05:30
end
2017-08-17 22:00:37 +05:30
desc 'Unarchive a project' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::Project
failure [
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
2022-07-16 23:28:13 +05:30
post ':id/unarchive', feature_category: :projects, urgency: :default do
2016-06-02 11:05:42 +05:30
authorize!(:archive_project, user_project)
2020-03-13 15:44:24 +05:30
::Projects::UpdateService.new(user_project, current_user, archived: false).execute
2016-06-02 11:05:42 +05:30
2021-11-11 11:23:49 +05:30
present_project user_project, with: Entities::Project, current_user: current_user
2016-06-02 11:05:42 +05:30
end
2017-08-17 22:00:37 +05:30
desc 'Star a project' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::Project
failure [
{ code: 304, message: 'Not modified' },
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post ':id/star', feature_category: :projects do
2016-06-02 11:05:42 +05:30
if current_user.starred?(user_project)
not_modified!
else
current_user.toggle_star(user_project)
2019-07-31 22:56:46 +05:30
user_project.reset
2016-06-02 11:05:42 +05:30
2021-11-11 11:23:49 +05:30
present_project user_project, with: Entities::Project, current_user: current_user
2016-06-02 11:05:42 +05:30
end
end
2017-08-17 22:00:37 +05:30
desc 'Unstar a project' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::Project
failure [
{ code: 304, message: 'Not modified' },
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post ':id/unstar', feature_category: :projects do
2016-06-02 11:05:42 +05:30
if current_user.starred?(user_project)
current_user.toggle_star(user_project)
2019-07-31 22:56:46 +05:30
user_project.reset
2016-06-02 11:05:42 +05:30
2021-11-11 11:23:49 +05:30
present_project user_project, with: Entities::Project, current_user: current_user
2016-06-02 11:05:42 +05:30
else
not_modified!
end
end
2019-10-12 21:52:04 +05:30
desc 'Get the users who starred a project' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::UserBasic
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
is_array true
tags %w[projects]
2019-10-12 21:52:04 +05:30
end
params do
2023-03-04 22:38:38 +05:30
optional :search, type: String, desc: 'Return list of users matching the search criteria', documentation: { example: 'user' }
2019-10-12 21:52:04 +05:30
use :pagination
end
2021-01-29 00:20:46 +05:30
get ':id/starrers', feature_category: :projects do
2019-10-12 21:52:04 +05:30
starrers = UsersStarProjectsFinder.new(user_project, params, current_user: current_user).execute
present paginate(starrers), with: Entities::UserStarsProject
end
2023-03-04 22:38:38 +05:30
desc 'Get languages in project repository' do
success code: 200
failure [
{ code: 404, message: 'Not found' }
]
is_array true
tags %w[projects]
end
2021-12-11 22:18:48 +05:30
get ':id/languages', feature_category: :source_code_management, urgency: :medium do
2019-07-07 11:18:12 +05:30
::Projects::RepositoryLanguagesService
.new(user_project, current_user)
2021-04-29 21:17:54 +05:30
.execute.to_h { |lang| [lang.name, lang.share] }
2018-10-15 14:42:47 +05:30
end
2023-03-04 22:38:38 +05:30
desc 'Delete a project' do
success code: 202
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[projects]
end
2021-01-29 00:20:46 +05:30
delete ":id", feature_category: :projects do
2014-09-02 18:07:02 +05:30
authorize! :remove_project, user_project
2018-03-17 18:26:18 +05:30
2020-01-01 13:55:28 +05:30
delete_project(user_project)
2014-09-02 18:07:02 +05:30
end
2023-03-04 22:38:38 +05:30
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
2017-08-17 22:00:37 +05:30
params do
2023-03-04 22:38:38 +05:30
requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from', documentation: { example: 'gitlab' }
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post ":id/fork/:forked_from_id", feature_category: :source_code_management do
2018-11-20 20:47:30 +05:30
authorize! :admin_project, user_project
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
fork_from_project = find_project!(params[:forked_from_id])
not_found!("Source Project") unless fork_from_project
2017-08-17 22:00:37 +05:30
2020-05-30 21:06:31 +05:30
authorize! :fork_project, fork_from_project
2018-03-17 18:26:18 +05:30
result = ::Projects::ForkService.new(fork_from_project, current_user).execute(user_project)
2017-09-10 17:25:29 +05:30
2018-03-17 18:26:18 +05:30
if result
2021-11-11 11:23:49 +05:30
present_project user_project.reset, with: Entities::Project, current_user: current_user
2023-03-04 22:38:38 +05:30
elsif user_project.forked?
render_api_error!("Project already forked", 409)
2014-09-02 18:07:02 +05:30
end
end
2023-03-04 22:38:38 +05:30
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
2021-01-29 00:20:46 +05:30
delete ":id/fork", feature_category: :source_code_management do
2015-11-26 14:37:03 +05:30
authorize! :remove_fork_project, user_project
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
result = destroy_conditionally!(user_project) do
::Projects::UnlinkForkService.new(user_project, current_user).execute
2014-09-02 18:07:02 +05:30
end
2018-03-17 18:26:18 +05:30
2020-03-13 15:44:24 +05:30
not_modified! unless result
2014-09-02 18:07:02 +05:30
end
2017-08-17 22:00:37 +05:30
desc 'Share the project with a group' do
2023-03-04 22:38:38 +05:30
success code: 201, model: Entities::ProjectGroupLink
failure [
{ code: 400, message: 'Bad request' },
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
params do
2023-03-04 22:38:38 +05:30
requires :group_id, type: Integer, desc: 'The ID of a group', documentation: { example: 1 }
2019-03-13 22:55:13 +05:30
requires :group_access, type: Integer, values: Gitlab::Access.values, as: :link_group_access, desc: 'The group access level'
2017-08-17 22:00:37 +05:30
optional :expires_at, type: Date, desc: 'Share expiration date'
end
2021-01-29 00:20:46 +05:30
post ":id/share", feature_category: :authentication_and_authorization do
2016-06-02 11:05:42 +05:30
authorize! :admin_project, user_project
2022-07-16 23:28:13 +05:30
shared_with_group = Group.find_by_id(params[:group_id])
2016-11-03 12:29:30 +05:30
2016-06-02 11:05:42 +05:30
unless user_project.allowed_to_share_with_group?
2018-10-15 14:42:47 +05:30
break render_api_error!("The project sharing with group is disabled", 400)
2016-06-02 11:05:42 +05:30
end
2022-07-16 23:28:13 +05:30
result = ::Projects::GroupLinks::CreateService
.new(user_project, shared_with_group, current_user, declared_params(include_missing: false)).execute
2016-11-03 12:29:30 +05:30
2019-03-13 22:55:13 +05:30
if result[:status] == :success
present result[:link], with: Entities::ProjectGroupLink
2016-06-02 11:05:42 +05:30
else
2019-03-13 22:55:13 +05:30
render_api_error!(result[:message], result[:http_status])
2016-06-02 11:05:42 +05:30
end
end
2023-03-04 22:38:38 +05:30
desc 'Remove a group share' do
success code: 204
failure [
{ code: 400, message: 'Bad request' },
{ code: 404, message: 'Not found' }
]
tags %w[projects]
end
2017-08-17 22:00:37 +05:30
params do
requires :group_id, type: Integer, desc: 'The ID of the group'
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2021-01-29 00:20:46 +05:30
delete ":id/share/:group_id", feature_category: :authentication_and_authorization do
2017-08-17 22:00:37 +05:30
authorize! :admin_project, user_project
2017-08-17 22:00:37 +05:30
link = user_project.project_group_links.find_by(group_id: params[:group_id])
not_found!('Group Link') unless link
2015-04-26 12:48:37 +05:30
2020-06-23 00:09:42 +05:30
destroy_conditionally!(link) do
::Projects::GroupLinks::DestroyService.new(user_project, current_user).execute(link)
end
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2017-08-17 22:00:37 +05:30
2021-10-27 15:23:28 +05:30
desc 'Import members from another project' do
detail 'This feature was introduced in GitLab 14.2'
2023-03-04 22:38:38 +05:30
success code: 201
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[projects]
2021-10-27 15:23:28 +05:30
end
params do
requires :project_id, type: Integer, desc: 'The ID of the source project to import the members from.'
end
2022-06-21 17:19:12 +05:30
post ":id/import_project_members/:project_id", feature_category: :projects do
::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/355916')
2021-10-27 15:23:28 +05:30
authorize! :admin_project, user_project
source_project = Project.find_by_id(params[:project_id])
not_found!('Project') unless source_project && can?(current_user, :read_project, source_project)
2022-01-12 12:59:36 +05:30
forbidden!('Project') unless source_project && can?(current_user, :admin_project_member, source_project)
2021-10-27 15:23:28 +05:30
result = ::Members::ImportProjectTeamService.new(current_user, params).execute
if result
{ status: result, message: 'Successfully imported' }
else
render_api_error!('Import failed', :unprocessable_entity)
end
end
2021-04-29 21:17:54 +05:30
desc 'Workhorse authorize the file upload' do
detail 'This feature was introduced in GitLab 13.11'
2023-03-04 22:38:38 +05:30
success code: 200
failure [
{ code: 404, message: 'Not found' }
]
tags %w[projects]
2021-04-29 21:17:54 +05:30
end
2022-06-21 17:19:12 +05:30
post ':id/uploads/authorize', feature_category: :not_owned do # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
2021-04-29 21:17:54 +05:30
require_gitlab_workhorse!
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
FileUploader.workhorse_authorize(has_length: false, maximum_size: project_attachment_size(user_project))
end
2023-03-04 22:38:38 +05:30
desc 'Upload a file' do
success code: 201, model: Entities::ProjectUpload
failure [
{ code: 404, message: 'Not found' }
]
tags %w[projects]
end
2017-08-17 22:00:37 +05:30
params do
2023-01-13 00:05:48 +05:30
requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded', documentation: { type: 'file' }
2017-08-17 22:00:37 +05:30
end
2022-06-21 17:19:12 +05:30
post ":id/uploads", feature_category: :not_owned do # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
2021-04-29 21:17:54 +05:30
log_if_upload_exceed_max_size(user_project, params[:file])
service = UploadService.new(user_project, params[:file])
service.override_max_attachment_size = project_attachment_size(user_project)
upload = service.execute
2020-04-08 14:13:33 +05:30
present upload, with: Entities::ProjectUpload
2014-09-02 18:07:02 +05:30
end
2017-08-17 22:00:37 +05:30
desc 'Get the users list of a project' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::UserBasic
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
is_array true
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
params do
2023-03-04 22:38:38 +05:30
optional :search, type: String, desc: 'Return list of users matching the search criteria', documentation: { example: 'user' }
2020-07-28 23:09:34 +05:30
optional :skip_users, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Filter out users with the specified IDs'
2017-08-17 22:00:37 +05:30
use :pagination
end
2022-07-16 23:28:13 +05:30
get ':id/users', urgency: :low, feature_category: :authentication_and_authorization do
2017-09-10 17:25:29 +05:30
users = DeclarativePolicy.subject_scope { user_project.team.users }
2017-08-17 22:00:37 +05:30
users = users.search(params[:search]) if params[:search].present?
2019-12-04 20:38:33 +05:30
users = users.where_not_in(params[:skip_users]) if params[:skip_users].present?
2021-11-18 22:05:49 +05:30
users = users.order('project_authorizations.user_id' => :asc) # rubocop: disable CodeReuse/ActiveRecord
2021-09-30 23:02:18 +05:30
2017-08-17 22:00:37 +05:30
present paginate(users), with: Entities::UserBasic
end
2021-04-17 20:07:23 +05:30
desc 'Get ancestor and shared groups for a project' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::PublicGroupDetails
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
is_array true
tags %w[projects]
2021-04-17 20:07:23 +05:30
end
params do
2023-03-04 22:38:38 +05:30
optional :search, type: String, desc: 'Return list of groups matching the search criteria', documentation: { example: 'group' }
2021-04-17 20:07:23 +05:30
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,
2022-08-27 11:52:29 +05:30
desc: 'Include shared groups'
2021-06-08 01:23:25 +05:30
optional :shared_visible_only, type: Boolean, default: false,
2022-08-27 11:52:29 +05:30
desc: 'Limit to shared groups user has access to'
2021-04-17 20:07:23 +05:30
optional :shared_min_access_level, type: Integer, values: Gitlab::Access.all_values,
2022-08-27 11:52:29 +05:30
desc: 'Limit returned shared groups by minimum access level to the project'
2021-04-17 20:07:23 +05:30
use :pagination
end
get ':id/groups', feature_category: :source_code_management do
groups = ::Projects::GroupsFinder.new(project: user_project, current_user: current_user, params: declared_params(include_missing: false)).execute
groups = groups.search(params[:search]) if params[:search].present?
present_groups groups
end
2017-08-17 22:00:37 +05:30
desc 'Start the housekeeping task for a project' do
detail 'This feature was introduced in GitLab 9.0.'
2023-03-04 22:38:38 +05:30
success code: 201
failure [
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Unauthenticated' },
{ code: 409, message: 'Conflict' }
]
tags %w[projects]
2017-08-17 22:00:37 +05:30
end
2021-01-29 00:20:46 +05:30
post ':id/housekeeping', feature_category: :source_code_management do
2017-08-17 22:00:37 +05:30
authorize_admin_project
begin
2021-03-08 18:12:59 +05:30
::Repositories::HousekeepingService.new(user_project, :gc).execute
rescue ::Repositories::HousekeepingService::LeaseTaken => error
2017-08-17 22:00:37 +05:30
conflict!(error.message)
end
2014-09-02 18:07:02 +05:30
end
2018-11-08 19:23:39 +05:30
2022-07-16 23:28:13 +05:30
desc 'Start a task to recalculate repository size for a project' do
detail 'This feature was introduced in GitLab 15.0.'
2023-03-04 22:38:38 +05:30
success code: 201
failure [
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
2022-07-16 23:28:13 +05:30
end
post ':id/repository_size', feature_category: :source_code_management do
authorize_admin_project
user_project.repository.expire_statistics_caches
::Projects::UpdateStatisticsService.new(user_project, nil, statistics: [:repository_size, :lfs_objects_size]).execute
end
2023-03-04 22:38:38 +05:30
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
2018-11-08 19:23:39 +05:30
params do
2023-03-04 22:38:38 +05:30
requires :namespace, type: String, desc: 'The ID or path of the new namespace', documentation: { example: 'gitlab' }
2018-11-08 19:23:39 +05:30
end
2021-01-29 00:20:46 +05:30
put ":id/transfer", feature_category: :projects do
2018-11-08 19:23:39 +05:30
authorize! :change_namespace, user_project
namespace = find_namespace!(params[:namespace])
result = ::Projects::TransferService.new(user_project, current_user).execute(namespace)
if result
2021-11-11 11:23:49 +05:30
present_project user_project, with: Entities::Project, current_user: current_user
2018-11-08 19:23:39 +05:30
else
render_api_error!("Failed to transfer project #{user_project.errors.messages}", 400)
end
end
2021-09-04 01:27:46 +05:30
2023-03-04 22:38:38 +05:30
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
2022-10-11 01:57:18 +05:30
params do
2023-03-04 22:38:38 +05:30
optional :search, type: String, desc: 'Return list of namespaces matching the search criteria', documentation: { example: 'search' }
2022-10-11 01:57:18 +05:30
use :pagination
end
get ":id/transfer_locations", feature_category: :projects do
authorize! :change_namespace, user_project
args = declared_params(include_missing: false)
args[:permission_scope] = :transfer_projects
groups = ::Groups::UserGroupsFinder.new(current_user, current_user, args).execute
groups = groups.with_route
present_groups(groups)
end
2021-09-04 01:27:46 +05:30
desc 'Show the storage information' do
2023-03-04 22:38:38 +05:30
success code: 200, model: Entities::ProjectRepositoryStorage
failure [
{ code: 403, message: 'Unauthenticated' }
]
tags %w[projects]
2021-09-04 01:27:46 +05:30
end
params do
requires :id, type: String, desc: 'ID of a project'
end
2022-07-16 23:28:13 +05:30
get ':id/storage', feature_category: :source_code_management do
2021-09-04 01:27:46 +05:30
authenticated_as_admin!
present user_project, with: Entities::ProjectRepositoryStorage, current_user: current_user
end
2014-09-02 18:07:02 +05:30
end
end
end
2019-12-04 20:38:33 +05:30
2021-06-08 01:23:25 +05:30
API::Projects.prepend_mod_with('API::Projects')