Skip to content
Snippets Groups Projects
Commit 64ae94e2 authored by Vijay Hawoldar's avatar Vijay Hawoldar
Browse files

Add new service for billable member removal

parent 98f9dc45
No related merge requests found
# frozen_string_literal: true
# RemoveBillableMemberService class
#
# Used to find and remove all Member records (GroupMember or ProjectMember)
# within a group's hierarchy for the given user_id, so that a billable member can be completely
# removed from the group and it's subgroups and projects
#
# Ex.
# Members::RemoveBillableMemberService.new(group, user_id: 1, current_user: current_user).execute
#
module Members
class RemoveBillableMemberService
include BaseServiceUtility
InvalidGroupError = Class.new(StandardError)
def initialize(group, user_id:, current_user:)
@group = group
@user_id = user_id
@current_user = current_user
end
def execute
check_group_level
check_user_access
remove_user_from_resources
success
rescue InvalidGroupError, Gitlab::Access::AccessDeniedError => e
error(e.message)
end
private
attr_reader :group, :user_id, :current_user
# rubocop: disable CodeReuse/ActiveRecord
def remove_user_from_resources
memberships = ::Member.in_hierarchy(group).where(user_id: user_id)
memberships.each do |member|
::Members::DestroyService.new(current_user).execute(member, skip_subresources: true)
end
end
# rubocop: enable CodeReuse/ActiveRecord
def check_group_level
unless group.root?
raise InvalidGroupError, 'Invalid group provided, must be top-level'
end
end
def check_user_access
unless can?(current_user, :admin_group_member, group)
raise Gitlab::Access::AccessDeniedError, 'User unauthorized to remove member'
end
end
end
end
......@@ -82,19 +82,17 @@ module Members
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
end
# rubocop: disable CodeReuse/ActiveRecord
delete ":id/billable_members/:user_id" do
group = find_group!(params[:id])
bad_request!(nil) unless ::Ability.allowed?(current_user, :admin_group_member, group)
member = ::Member.in_hierarchy(group).find_by!(user_id: params[:user_id])
result = ::Members::RemoveBillableMemberService.new(group, user_id: params[:user_id], current_user: current_user).execute
destroy_conditionally!(member) do
::Members::DestroyService.new(current_user).execute(member)
if result[:status] == :success
no_content!
else
bad_request!(nil)
end
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Members::RemoveBillableMemberService do
describe '#execute' do
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) }
let(:user_id) { nil }
subject(:execute) { described_class.new(group, user_id: user_id, current_user: current_user).execute }
context 'when unauthorized' do
it 'raises an access error' do
result = execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'User unauthorized to remove member'
end
end
context 'when authorized' do
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:project_1) { create(:project, group: subgroup) }
let_it_be(:project_2) { create(:project, group: group) }
let_it_be(:group_member) { create(:group_member, group: group) }
let_it_be(:subgroup_member) { create(:group_member, group: subgroup) }
let_it_be(:project_member) { create(:project_member, project: project_1) }
before do
group.add_owner(current_user)
end
context 'when passing a sub group to the service' do
let(:group) { subgroup }
it 'raises an invalid group error' do
result = execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'Invalid group provided, must be top-level'
end
end
context 'when removing a group member' do
let(:user_id) { group_member.user_id }
it 'removes the member' do
execute
expect(group.members).not_to include(group_member)
end
end
context 'when removing a subgroup member' do
let(:user_id) { subgroup_member.user_id }
it 'removes the member' do
execute
expect(subgroup.members).not_to include(subgroup_member)
end
end
context 'when removing a project member' do
let(:user_id) { project_member.user_id }
it 'removes the member' do
execute
expect(project_1.members).not_to include(project_member)
end
end
context 'when the user is a direct member of multiple projects' do
let(:multi_project_user) { create(:user) }
let(:user_id) { multi_project_user.id }
it 'removes the user from all the projects' do
project_1.add_developer(multi_project_user)
project_2.add_developer(multi_project_user)
execute
expect(multi_project_user.projects).not_to include(project_1)
expect(multi_project_user.projects).not_to include(project_2)
end
end
end
end
end
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment