2018-09-29 18:34:47 -04:00
# frozen_string_literal: true
2013-05-14 08:33:31 -04:00
module API
2020-10-14 20:08:42 -04:00
class Groups < :: API :: Base
2016-12-04 12:11:19 -05:00
include PaginationParams
2017-12-22 10:54:55 -05:00
include Helpers :: CustomAttributes
2016-12-12 03:55:29 -05:00
2017-08-23 08:01:11 -04:00
before { authenticate_non_get! }
2013-01-08 16:05:00 -05:00
2019-05-27 08:37:53 -04:00
helpers Helpers :: GroupsHelpers
2019-05-20 13:02:06 -04:00
2022-04-14 08:09:31 -04:00
feature_category :subgroups , [ '/groups/:id/custom_attributes' , '/groups/:id/custom_attributes/:key' ]
2019-05-20 13:02:06 -04:00
helpers do
2016-11-22 11:58:10 -05:00
params :statistics_params do
optional :statistics , type : Boolean , default : false , desc : 'Include project statistics'
end
2017-11-09 11:07:04 -05:00
params :group_list_params do
2016-11-22 11:58:10 -05:00
use :statistics_params
2020-06-29 17:09:07 -04:00
optional :skip_groups , type : Array [ Integer ] , coerce_with : :: API :: Validations :: Types :: CommaSeparatedToIntegerArray . coerce , desc : 'Array of group ids to exclude from list'
2016-11-07 10:54:39 -05:00
optional :all_available , type : Boolean , desc : 'Show all group that you have access to'
optional :search , type : String , desc : 'Search for a specific group'
2017-02-24 03:25:53 -05:00
optional :owned , type : Boolean , default : false , desc : 'Limit by owned by authenticated user'
2021-06-17 11:10:03 -04:00
optional :order_by , type : String , values : %w[ name path id similarity ] , default : 'name' , desc : 'Order by name, path, id or similarity if searching'
2016-11-17 07:41:48 -05:00
optional :sort , type : String , values : %w[ asc desc ] , default : 'asc' , desc : 'Sort by asc (ascending) or desc (descending)'
2018-07-08 15:41:55 -04:00
optional :min_access_level , type : Integer , values : Gitlab :: Access . all_values , desc : 'Minimum access level of authenticated user'
2020-05-25 23:08:02 -04:00
optional :top_level_only , type : Boolean , desc : 'Only include top level groups'
2016-12-04 12:11:19 -05:00
use :pagination
2016-11-07 10:54:39 -05:00
end
2017-11-09 11:07:04 -05:00
2018-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ActiveRecord
2018-05-01 05:24:21 -04:00
def find_groups ( params , parent_id = nil )
2020-09-30 02:09:47 -04:00
find_params = params . slice (
:all_available ,
:custom_attributes ,
:owned , :min_access_level ,
2021-07-22 20:09:55 -04:00
:include_parent_descendants ,
:search
2020-09-30 02:09:47 -04:00
)
2020-05-25 23:08:02 -04:00
find_params [ :parent ] = if params [ :top_level_only ]
[ nil ]
elsif parent_id
find_group! ( parent_id )
end
2018-05-01 05:24:21 -04:00
find_params [ :all_available ] =
2019-12-17 04:07:48 -05:00
find_params . fetch ( :all_available , current_user & . can_read_all_resources? )
2017-09-18 11:07:38 -04:00
2017-08-24 06:33:06 -04:00
groups = GroupsFinder . new ( current_user , find_params ) . execute
2016-11-07 10:54:39 -05:00
groups = groups . where . not ( id : params [ :skip_groups ] ) if params [ :skip_groups ] . present?
2021-06-17 11:10:03 -04:00
order_groups ( groups )
2017-11-09 11:07:04 -05:00
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ActiveRecord
2017-11-09 11:07:04 -05:00
2019-02-25 09:01:34 -05:00
def create_group
# This is a separate method so that EE can extend its behaviour, without
# having to modify this code directly.
:: Groups :: CreateService
. new ( current_user , declared_params ( include_missing : false ) )
. execute
end
2019-02-25 09:17:17 -05:00
def update_group ( group )
# This is a separate method so that EE can extend its behaviour, without
# having to modify this code directly.
:: Groups :: UpdateService
. new ( group , current_user , declared_params ( include_missing : false ) )
. execute
end
2020-04-28 11:09:29 -04:00
def find_group_projects ( params , finder_options )
2017-11-24 07:20:52 -05:00
group = find_group! ( params [ :id ] )
2018-10-26 02:02:20 -04:00
projects = GroupProjectsFinder . new (
group : group ,
current_user : current_user ,
params : project_finder_params ,
2020-04-28 11:09:29 -04:00
options : finder_options
2018-10-26 02:02:20 -04:00
) . execute
2020-07-23 05:09:18 -04:00
projects = reorder_projects_with_similarity_order_support ( group , projects )
2017-11-27 06:24:33 -05:00
paginate ( projects )
2017-11-24 07:20:52 -05:00
end
2022-01-25 16:15:18 -05:00
def present_projects ( params , projects , single_hierarchy : false )
2020-04-28 11:09:29 -04:00
options = {
with : params [ :simple ] ? Entities :: BasicProjectDetails : Entities :: Project ,
2022-01-25 16:15:18 -05:00
current_user : current_user ,
single_hierarchy : single_hierarchy
2020-04-28 11:09:29 -04:00
}
projects , options = with_custom_attributes ( projects , options )
2021-10-05 11:12:53 -04:00
present options [ :with ] . prepare_relation ( projects , options ) , options
2020-04-28 11:09:29 -04:00
end
2022-08-23 20:12:25 -04:00
def present_groups ( params , groups , serializer : Entities :: Group )
2017-11-09 11:07:04 -05:00
options = {
2022-08-23 20:12:25 -04:00
with : serializer ,
2017-11-09 11:07:04 -05:00
current_user : current_user ,
2020-05-15 08:08:28 -04:00
statistics : params [ :statistics ] && current_user & . admin?
2017-11-09 11:07:04 -05:00
}
groups = groups . with_statistics if options [ :statistics ]
2017-12-22 10:54:55 -05:00
groups , options = with_custom_attributes ( groups , options )
2017-11-09 11:07:04 -05:00
present paginate ( groups ) , options
end
2020-01-24 07:09:01 -05:00
2022-06-21 23:08:59 -04:00
def present_group_details ( params , group , with_projects : true )
options = {
with : Entities :: GroupDetail ,
with_projects : with_projects ,
current_user : current_user ,
user_can_admin_group : can? ( current_user , :admin_group , group )
}
group , options = with_custom_attributes ( group , options ) if params [ :with_custom_attributes ]
present group , options
end
2021-09-07 05:11:43 -04:00
def present_groups_with_pagination_strategies ( params , groups )
2021-11-26 07:12:49 -05:00
return present_groups ( params , groups ) if current_user . present?
2021-09-07 05:11:43 -04:00
options = {
with : Entities :: Group ,
current_user : nil ,
statistics : false
}
groups , options = with_custom_attributes ( groups , options )
present paginate_with_strategies ( groups ) , options
end
2020-01-24 07:09:01 -05:00
def delete_group ( group )
destroy_conditionally! ( group ) do | group |
:: Groups :: DestroyService . new ( group , current_user ) . async_execute
end
accepted!
end
2020-07-23 05:09:18 -04:00
def reorder_projects_with_similarity_order_support ( group , projects )
return handle_similarity_order ( group , projects ) if params [ :order_by ] == 'similarity'
reorder_projects ( projects )
end
2021-06-17 11:10:03 -04:00
def order_groups ( groups )
return groups . sorted_by_similarity_and_parent_id_desc ( params [ :search ] ) if order_by_similarity?
groups . reorder ( group_without_similarity_options ) # rubocop: disable CodeReuse/ActiveRecord
end
def group_without_similarity_options
order_options = { params [ :order_by ] = > params [ :sort ] }
order_options [ 'name' ] = order_options . delete ( 'similarity' ) if order_options . has_key? ( 'similarity' )
order_options [ " id " ] || = " asc "
order_options
end
2020-07-23 05:09:18 -04:00
# rubocop: disable CodeReuse/ActiveRecord
def handle_similarity_order ( group , projects )
2021-08-06 08:10:15 -04:00
if params [ :search ] . present?
2020-07-23 05:09:18 -04:00
projects . sorted_by_similarity_desc ( params [ :search ] )
else
order_options = { name : :asc }
order_options [ 'id' ] || = params [ :sort ] || 'asc'
projects . reorder ( order_options )
end
end
# rubocop: enable CodeReuse/ActiveRecord
2021-03-12 13:09:23 -05:00
def authorize_group_creation!
authorize! :create_group
end
2021-03-29 08:09:14 -04:00
def check_subscription! ( group )
render_api_error! ( " This group can't be removed because it is linked to a subscription. " , :bad_request ) if group . paid?
end
2017-11-09 11:07:04 -05:00
end
resource :groups do
include CustomAttributesEndpoints
desc 'Get a groups list' do
success Entities :: Group
end
params do
use :group_list_params
2017-12-22 10:54:55 -05:00
use :with_custom_attributes
2017-11-09 11:07:04 -05:00
end
2022-04-14 08:09:31 -04:00
get feature_category : :subgroups do
2018-05-01 05:24:21 -04:00
groups = find_groups ( declared_params ( include_missing : false ) , params [ :id ] )
2021-09-07 05:11:43 -04:00
present_groups_with_pagination_strategies params , groups
2013-02-01 09:00:12 -05:00
end
2016-11-07 10:54:39 -05:00
desc 'Create a group. Available only for users who can create groups.' do
success Entities :: Group
end
params do
requires :name , type : String , desc : 'The name of the group'
requires :path , type : String , desc : 'The path of the group'
2019-07-24 05:20:54 -04:00
optional :parent_id , type : Integer , desc : 'The parent group id for creating nested group'
2017-05-03 08:49:37 -04:00
2016-11-07 10:54:39 -05:00
use :optional_params
end
2022-04-22 14:09:22 -04:00
post feature_category : :subgroups , urgency : :low do
2017-09-07 14:35:45 -04:00
parent_group = find_group! ( params [ :parent_id ] ) if params [ :parent_id ] . present?
if parent_group
authorize! :create_subgroup , parent_group
else
2021-03-12 13:09:23 -05:00
authorize_group_creation!
2017-09-07 14:35:45 -04:00
end
2013-02-27 06:34:45 -05:00
2019-02-25 09:01:34 -05:00
group = create_group
2020-06-09 14:08:28 -04:00
group . preload_shared_group_links
2013-02-01 09:00:12 -05:00
2016-11-07 10:54:39 -05:00
if group . persisted?
2017-06-06 09:42:45 -04:00
present group , with : Entities :: GroupDetail , current_user : current_user
2013-02-01 09:00:12 -05:00
else
2016-11-07 10:54:39 -05:00
render_api_error! ( " Failed to save group #{ group . errors . messages } " , 400 )
2013-02-01 09:00:12 -05:00
end
end
2016-11-07 10:54:39 -05:00
end
2013-02-01 09:00:12 -05:00
2016-11-07 10:54:39 -05:00
params do
requires :id , type : String , desc : 'The ID of a group'
end
2018-11-08 07:18:17 -05:00
resource :groups , requirements : API :: NAMESPACE_OR_PROJECT_REQUIREMENTS do
2016-11-07 10:54:39 -05:00
desc 'Update a group. Available only for users who can administrate groups.' do
success Entities :: Group
end
params do
optional :name , type : String , desc : 'The name of the group'
optional :path , type : String , desc : 'The path of the group'
use :optional_params
2021-06-23 08:07:58 -04:00
use :optional_update_params
2019-05-20 13:02:06 -04:00
use :optional_update_params_ee
2016-11-07 10:54:39 -05:00
end
2022-04-22 14:09:22 -04:00
put ':id' , feature_category : :subgroups , urgency : :low do
2016-11-24 10:58:32 -05:00
group = find_group! ( params [ :id ] )
2020-06-09 14:08:28 -04:00
group . preload_shared_group_links
2016-04-07 04:12:49 -04:00
authorize! :admin_group , group
2019-02-25 09:17:17 -05:00
if update_group ( group )
2022-06-21 23:08:59 -04:00
present_group_details ( params , group , with_projects : true )
2016-04-12 13:08:35 -04:00
else
render_validation_error! ( group )
2016-04-07 04:12:49 -04:00
end
end
2016-11-07 10:54:39 -05:00
desc 'Get a single group, with containing projects.' do
success Entities :: GroupDetail
end
2017-12-22 10:54:55 -05:00
params do
use :with_custom_attributes
2018-07-09 08:24:29 -04:00
optional :with_projects , type : Boolean , default : true , desc : 'Omit project details'
2017-12-22 10:54:55 -05:00
end
2022-04-19 11:08:32 -04:00
# TODO: Set higher urgency after resolving https://gitlab.com/gitlab-org/gitlab/-/issues/357841
get " :id " , feature_category : :subgroups , urgency : :low do
2016-11-24 10:58:32 -05:00
group = find_group! ( params [ :id ] )
2020-06-09 14:08:28 -04:00
group . preload_shared_group_links
2017-12-22 10:54:55 -05:00
2022-06-21 23:08:59 -04:00
present_group_details ( params , group , with_projects : params [ :with_projects ] )
2013-02-01 09:00:12 -05:00
end
2012-11-14 15:37:52 -05:00
2016-11-07 10:54:39 -05:00
desc 'Remove a group.'
2022-04-22 05:09:00 -04:00
delete " :id " , feature_category : :subgroups , urgency : :low do
2016-11-24 10:58:32 -05:00
group = find_group! ( params [ :id ] )
2015-04-10 08:39:10 -04:00
authorize! :admin_group , group
2021-03-29 08:09:14 -04:00
check_subscription! group
2017-07-20 09:33:18 -04:00
2020-01-24 07:09:01 -05:00
delete_group ( group )
2013-10-07 06:10:01 -04:00
end
2016-11-07 10:54:39 -05:00
desc 'Get a list of projects in this group.' do
success Entities :: Project
end
2016-12-04 12:11:19 -05:00
params do
2020-07-03 14:08:58 -04:00
optional :archived , type : Boolean , desc : 'Limit by archived status'
2017-02-16 08:56:14 -05:00
optional :visibility , type : String , values : Gitlab :: VisibilityLevel . string_values ,
2016-12-12 03:55:29 -05:00
desc : 'Limit by visibility'
optional :search , type : String , desc : 'Return list of authorized projects matching the search criteria'
2020-07-23 05:09:18 -04:00
optional :order_by , type : String , values : %w[ id name path created_at updated_at last_activity_at similarity ] ,
2016-12-12 03:55:29 -05:00
default : 'created_at' , desc : 'Return projects ordered by field'
optional :sort , type : String , values : %w[ asc desc ] , default : 'desc' ,
desc : 'Return projects sorted in ascending and descending order'
2016-12-13 07:51:30 -05:00
optional :simple , type : Boolean , default : false ,
desc : 'Return only the ID, URL, name, and path of each project'
2017-02-01 05:23:57 -05:00
optional :owned , type : Boolean , default : false , desc : 'Limit by owned by authenticated user'
optional :starred , type : Boolean , default : false , desc : 'Limit by starred status'
2018-07-02 04:04:43 -04:00
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'
2018-10-26 02:34:31 -04:00
optional :with_shared , type : Boolean , default : true , desc : 'Include projects shared to this group'
2018-10-26 02:02:20 -04:00
optional :include_subgroups , type : Boolean , default : false , desc : 'Includes projects in subgroups of this group'
2022-02-10 16:15:20 -05:00
optional :include_ancestor_groups , type : Boolean , default : false , desc : 'Includes projects in ancestors of this group'
2019-09-04 12:33:02 -04:00
optional :min_access_level , type : Integer , values : Gitlab :: Access . all_values , desc : 'Limit by minimum access level of authenticated user on projects'
2017-02-01 05:23:57 -05:00
2016-12-04 12:11:19 -05:00
use :pagination
2017-12-22 10:54:55 -05:00
use :with_custom_attributes
2019-08-28 10:26:42 -04:00
use :optional_projects_params
2016-12-04 12:11:19 -05:00
end
2022-04-19 11:08:32 -04:00
# TODO: Set higher urgency after resolving https://gitlab.com/gitlab-org/gitlab/-/issues/211498
get " :id/projects " , feature_category : :subgroups , urgency : :low do
2020-04-28 11:09:29 -04:00
finder_options = {
only_owned : ! params [ :with_shared ] ,
2022-02-10 16:15:20 -05:00
include_subgroups : params [ :include_subgroups ] ,
include_ancestor_groups : params [ :include_ancestor_groups ]
2017-12-22 10:54:55 -05:00
}
2020-04-28 11:09:29 -04:00
projects = find_group_projects ( params , finder_options )
2017-12-22 10:54:55 -05:00
2022-01-25 16:15:18 -05:00
present_projects ( params , projects , single_hierarchy : true )
2020-04-28 11:09:29 -04:00
end
desc 'Get a list of shared projects in this group' do
success Entities :: Project
end
params do
2020-07-03 14:08:58 -04:00
optional :archived , type : Boolean , desc : 'Limit by archived status'
2020-04-28 11:09:29 -04:00
optional :visibility , type : String , values : Gitlab :: VisibilityLevel . string_values ,
desc : 'Limit by visibility'
optional :search , type : String , desc : 'Return list of authorized projects matching the search criteria'
optional :order_by , type : String , values : %w[ id name path created_at updated_at last_activity_at ] ,
default : 'created_at' , desc : 'Return projects ordered by field'
optional :sort , type : String , values : %w[ asc desc ] , default : 'desc' ,
desc : 'Return projects sorted in ascending and descending order'
optional :simple , type : Boolean , default : false ,
desc : 'Return only the ID, URL, name, and path of each project'
optional :starred , type : Boolean , default : false , desc : 'Limit by starred status'
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'
optional :min_access_level , type : Integer , values : Gitlab :: Access . all_values , desc : 'Limit by minimum access level of authenticated user on projects'
use :pagination
use :with_custom_attributes
end
2022-04-14 08:09:31 -04:00
get " :id/projects/shared " , feature_category : :subgroups do
2020-04-28 11:09:29 -04:00
projects = find_group_projects ( params , { only_shared : true } )
present_projects ( params , projects )
2015-12-07 11:10:40 -05:00
end
2017-11-09 11:07:04 -05:00
desc 'Get a list of subgroups in this group.' do
success Entities :: Group
end
params do
use :group_list_params
2017-12-22 10:54:55 -05:00
use :with_custom_attributes
2017-11-09 11:07:04 -05:00
end
2022-04-19 11:08:32 -04:00
get " :id/subgroups " , feature_category : :subgroups , urgency : :low do
2018-05-01 05:24:21 -04:00
groups = find_groups ( declared_params ( include_missing : false ) , params [ :id ] )
2017-11-09 11:07:04 -05:00
present_groups params , groups
end
2020-09-30 02:09:47 -04:00
desc 'Get a list of descendant groups of this group.' do
success Entities :: Group
end
params do
use :group_list_params
use :with_custom_attributes
end
2022-04-28 11:10:08 -04:00
get " :id/descendant_groups " , feature_category : :subgroups , urgency : :low do
2020-09-30 02:09:47 -04:00
finder_params = declared_params ( include_missing : false ) . merge ( include_parent_descendants : true )
groups = find_groups ( finder_params , params [ :id ] )
present_groups params , groups
end
2016-11-07 10:54:39 -05:00
desc 'Transfer a project to the group namespace. Available only for admin.' do
success Entities :: GroupDetail
end
params do
2017-01-02 13:19:47 -05:00
requires :project_id , type : String , desc : 'The ID or path of the project'
2016-11-07 10:54:39 -05:00
end
2022-04-14 08:09:31 -04:00
post " :id/projects/:project_id " , requirements : { project_id : / .+ / } , feature_category : :projects do
2012-11-14 15:37:52 -05:00
authenticated_as_admin!
2017-01-02 13:19:47 -05:00
group = find_group! ( params [ :id ] )
2020-06-09 14:08:28 -04:00
group . preload_shared_group_links
2017-01-02 13:19:47 -05:00
project = find_project! ( params [ :project_id ] )
2015-07-02 08:31:25 -04:00
result = :: Projects :: TransferService . new ( project , current_user ) . execute ( group )
2014-05-28 12:03:45 -04:00
if result
2016-11-29 14:21:39 -05:00
present group , with : Entities :: GroupDetail , current_user : current_user
2012-11-14 15:37:52 -05:00
else
2015-01-07 04:46:00 -05:00
render_api_error! ( " Failed to transfer project #{ project . errors . messages } " , 400 )
2012-11-14 15:37:52 -05:00
end
2013-03-20 17:46:30 -04:00
end
2020-06-09 14:08:28 -04:00
2022-08-23 20:12:25 -04:00
desc 'Get the groups to where the current group can be transferred to'
params do
optional :search , type : String , desc : 'Return list of namespaces matching the search criteria'
use :pagination
end
get ':id/transfer_locations' , feature_category : :subgroups do
authorize! :admin_group , user_group
args = declared_params ( include_missing : false )
groups = :: Groups :: AcceptingGroupTransfersFinder . new ( current_user , user_group , args ) . execute
groups = groups . with_route
present_groups params , groups , serializer : Entities :: PublicGroupDetails
end
2021-12-02 13:11:52 -05:00
desc 'Transfer a group to a new parent group or promote a subgroup to a root group'
params do
2022-08-10 20:09:45 -04:00
optional :group_id ,
type : Integer ,
desc : 'The ID of the target group to which the group needs to be transferred to.' \
'If not provided, the source group will be promoted to a root group.'
2021-12-02 13:11:52 -05:00
end
2022-04-14 08:09:31 -04:00
post ':id/transfer' , feature_category : :subgroups do
2021-12-02 13:11:52 -05:00
group = find_group! ( params [ :id ] )
authorize! :admin_group , group
new_parent_group = find_group! ( params [ :group_id ] ) if params [ :group_id ] . present?
service = :: Groups :: TransferService . new ( group , current_user )
if service . execute ( new_parent_group )
group . preload_shared_group_links
present group , with : Entities :: GroupDetail , current_user : current_user
else
render_api_error! ( service . error , 400 )
end
end
2020-06-09 14:08:28 -04:00
desc 'Share a group with a group' do
success Entities :: GroupDetail
end
params do
requires :group_id , type : Integer , desc : 'The ID of the group to share'
requires :group_access , type : Integer , values : Gitlab :: Access . all_values , desc : 'The group access level'
optional :expires_at , type : Date , desc : 'Share expiration date'
end
2022-05-20 17:08:53 -04:00
post " :id/share " , feature_category : :subgroups , urgency : :low do
2020-06-09 14:08:28 -04:00
shared_with_group = find_group! ( params [ :group_id ] )
group_link_create_params = {
shared_group_access : params [ :group_access ] ,
expires_at : params [ :expires_at ]
}
2022-04-29 17:10:44 -04:00
result = :: Groups :: GroupLinks :: CreateService . new ( user_group , shared_with_group , current_user , group_link_create_params ) . execute
user_group . preload_shared_group_links
2020-06-09 14:08:28 -04:00
if result [ :status ] == :success
2022-04-29 17:10:44 -04:00
present user_group , with : Entities :: GroupDetail , current_user : current_user
2020-06-09 14:08:28 -04:00
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
end
params do
requires :group_id , type : Integer , desc : 'The ID of the shared group'
end
# rubocop: disable CodeReuse/ActiveRecord
2022-04-14 08:09:31 -04:00
delete " :id/share/:group_id " , feature_category : :subgroups do
2020-06-09 14:08:28 -04:00
shared_group = find_group! ( params [ :id ] )
link = shared_group . shared_with_group_links . find_by ( shared_with_group_id : params [ :group_id ] )
not_found! ( 'Group Link' ) unless link
:: Groups :: GroupLinks :: DestroyService . new ( shared_group , current_user ) . execute ( link )
no_content!
end
# rubocop: enable CodeReuse/ActiveRecord
2013-02-01 09:00:12 -05:00
end
2013-01-08 16:05:00 -05:00
end
end
2019-09-13 09:26:31 -04:00
2021-05-11 17:10:21 -04:00
API :: Groups . prepend_mod_with ( 'API::Groups' )