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 )
2020-07-28 23:09:34 +05:30
2021-10-27 15:23:28 +05:30
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
success Entities :: BasicProjectDetails
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
success Entities :: BasicProjectDetails
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
success Entities :: BasicProjectDetails
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
success Entities :: Project
end
params do
optional :name , type : String , desc : 'The name of the project'
optional :path , type : String , desc : 'The path of the repository'
2021-09-04 01:27:46 +05:30
optional :default_branch , type : String , desc : 'The default branch of the project'
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
success Entities :: Project
end
params do
requires :name , type : String , desc : 'The name of the project'
requires :user_id , type : Integer , desc : 'The ID of a user'
2017-09-10 17:25:29 +05:30
optional :path , type : String , desc : 'The path of the repository'
2017-08-17 22:00:37 +05:30
optional :default_branch , type : String , desc : 'The default branch of the project'
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
requires :id , type : String , desc : 'The ID of a project'
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
success Entities :: ProjectWithAccess
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
success Entities :: Project
end
params do
2020-04-08 14:13:33 +05:30
optional :namespace , type : String , desc : '(deprecated) The ID or name of the namespace that the project will be forked into'
optional :namespace_id , type : Integer , desc : 'The ID of the namespace that the project will be forked into'
optional :namespace_path , type : String , desc : 'The path of the namespace that the project will be forked into'
2019-07-07 11:18:12 +05:30
optional :path , type : String , desc : 'The path that will be assigned to the fork'
optional :name , type : String , desc : 'The name that will be assigned to the fork'
2021-03-11 19:13:27 +05:30
optional :description , type : String , desc : 'The description that will be assigned to the fork'
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
success Entities :: Project
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
2018-12-05 23:21:45 +05:30
desc 'Check pages access of this project'
2021-01-29 00:20:46 +05:30
get ':id/pages_access' , 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
success Entities :: Project
end
params do
optional :name , type : String , desc : 'The name of the project'
optional :default_branch , type : String , desc : 'The default branch of the project'
optional :path , type : String , desc : 'The path of the repository'
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 )
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
success Entities :: Project
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
success Entities :: Project
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
success Entities :: Project
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
success Entities :: Project
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
success Entities :: UserBasic
end
params do
optional :search , type : String , desc : 'Return list of users matching the search criteria'
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
2018-10-15 14:42:47 +05:30
desc 'Get languages in project repository'
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
2020-10-24 23:57:45 +05:30
desc 'Delete a project'
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
2017-08-17 22:00:37 +05:30
desc 'Mark this project as forked from another'
params do
requires :forked_from_id , type : String , desc : 'The ID of the project it was forked from'
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
2014-09-02 18:07:02 +05:30
else
2018-03-17 18:26:18 +05:30
render_api_error! ( " Project already forked " , 409 ) if user_project . forked?
2014-09-02 18:07:02 +05:30
end
end
2017-08-17 22:00:37 +05:30
desc 'Remove a forked_from relationship'
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
2016-01-14 18:37:52 +05:30
2017-08-17 22:00:37 +05:30
desc 'Share the project with a group' do
success Entities :: ProjectGroupLink
end
params do
requires :group_id , type : Integer , desc : 'The ID of a group'
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
2017-08-17 22:00:37 +05:30
params do
requires :group_id , type : Integer , desc : 'The ID of the group'
2016-01-14 18:37:52 +05:30
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
2016-01-14 18:37:52 +05:30
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'
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'
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
2017-08-17 22:00:37 +05:30
desc 'Upload a file'
params do
2021-04-29 21:17:54 +05:30
requires :file , types : [ Rack :: Multipart :: UploadedFile , :: API :: Validations :: Types :: WorkhorseFile ] , desc : 'The attachment file to be uploaded'
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
success Entities :: UserBasic
end
params do
optional :search , type : String , desc : 'Return list of users matching the search criteria'
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
success Entities :: PublicGroupDetails
end
params do
optional :search , type : String , desc : 'Return list of groups matching the search criteria'
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.'
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.'
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
2018-11-08 19:23:39 +05:30
desc 'Transfer a project to a new namespace'
params do
requires :namespace , type : String , desc : 'The ID or path of the new namespace'
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
2022-10-11 01:57:18 +05:30
desc 'Get the namespaces to where the project can be transferred'
params do
optional :search , type : String , desc : 'Return list of namespaces matching the search criteria'
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
success Entities :: ProjectRepositoryStorage
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' )