diff --git a/ee/app/services/ee/groups/transfer_service.rb b/ee/app/services/ee/groups/transfer_service.rb index ef2cafd060a1d65b844587d10f0de4e2747b074e..b890a1e35ad62ad0c8735f963fe47758d963c14d 100644 --- a/ee/app/services/ee/groups/transfer_service.rb +++ b/ee/app/services/ee/groups/transfer_service.rb @@ -66,6 +66,7 @@ def post_update_hooks(updated_project_ids, old_root_ancestor_id) process_group_associations(old_root_ancestor_id, group) # Epics and WorkItems sync_security_policies(group, current_user) + reconcile_seat_assignments(old_root_ancestor_id, group) end def sync_security_policies(group, current_user) @@ -184,6 +185,13 @@ def remove_project_compliance_frameworks(project) ) end end + + def reconcile_seat_assignments(old_root_ancestor_id, group) + return unless ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) + return if old_root_ancestor_id == group.root_ancestor.id + + GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateGroupSeatsWorker.perform_async(group.id) + end end end end diff --git a/ee/app/services/ee/projects/transfer_service.rb b/ee/app/services/ee/projects/transfer_service.rb index ede11fcef30899f13f8f8f06d80a88d5e66e0fbf..b71c8a7def726125786aac0cb3c08ea6a8cbca68 100644 --- a/ee/app/services/ee/projects/transfer_service.rb +++ b/ee/app/services/ee/projects/transfer_service.rb @@ -34,6 +34,7 @@ def post_update_hooks(project, old_group) update_compliance_standards_adherence delete_compliance_statuses sync_security_policies + reconcile_seat_assignments(old_namespace, project) end override :remove_paid_features @@ -82,6 +83,13 @@ def transfer_status_data project_namespace_ids: [project.project_namespace_id] ).execute end + + def reconcile_seat_assignments(old_namespace, project) + return unless ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) + return if old_namespace.root_ancestor.id == project.root_ancestor.id + + GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateProjectSeatsWorker.perform_async(project.id) + end end end end diff --git a/ee/spec/services/ee/groups/transfer_service_spec.rb b/ee/spec/services/ee/groups/transfer_service_spec.rb index 04088cb991c60f883b98e5ce01f96f06e569b49b..5a978bbab68f47fc8346b994ee9a3bbf943ea839 100644 --- a/ee/spec/services/ee/groups/transfer_service_spec.rb +++ b/ee/spec/services/ee/groups/transfer_service_spec.rb @@ -618,4 +618,64 @@ end end end + + describe 'subscription seat assignments reconciliation' do + before do + stub_saas_features(gitlab_com_subscriptions: true) + end + + context 'when a top level group becomes subgroup' do + it 'enqueues CreateGroupSeatsWorker worker with the group being transferred' do + expect(group.root_ancestor).to eq(group) + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateGroupSeatsWorker) + .to receive(:perform_async).with(group.id) + + transfer_service.execute(new_group) + expect(group.root_ancestor).to eq(new_group) + end + end + + context 'when a subgroup becomes top level group' do + let(:subgroup) { create(:group, :private, parent: group) } + let(:transfer_service) { described_class.new(subgroup, user) } + + it 'enqueues CreateGroupSeatsWorker worker with the new namespace (the group being transferred)' do + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateGroupSeatsWorker) + .to receive(:perform_async).with(subgroup.id) + + transfer_service.execute(nil) + + expect(subgroup.root_ancestor).to eq(subgroup) + end + end + + context 'when a sub-group becomes a sub-group in a different hierarchy' do + let(:subgroup) { create(:group, :private, parent: group) } + let(:transfer_service) { described_class.new(subgroup, user) } + + it 'enqueues CreateGroupSeatsWorker worker with the group being transferred in the params' do + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateGroupSeatsWorker) + .to receive(:perform_async).with(subgroup.id) + + transfer_service.execute(new_group) + + expect(subgroup.root_ancestor).to eq(new_group) + end + end + + context 'when a sub-group becomes a sub-group in the same hierarchy' do + let(:subgroup_to_be_transferred) { create(:group, :private, parent: group) } + let(:subgroup_to_be_new_parent) { create(:group, :private, parent: group) } + let(:transfer_service) { described_class.new(subgroup_to_be_transferred, user) } + + it 'does not enqueue CreateGroupSeatsWorker cleanup worker' do + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateGroupSeatsWorker) + .not_to receive(:perform_async).with(subgroup_to_be_transferred.id) + + transfer_service.execute(subgroup_to_be_new_parent) + + expect(subgroup_to_be_transferred.root_ancestor).to eq(group) + end + end + end end diff --git a/ee/spec/services/projects/transfer_service_spec.rb b/ee/spec/services/projects/transfer_service_spec.rb index 62705f333946f5a934b536ecf58e59b1adcf650f..94b81add9bcbb3fcba9003d51f9045d3db710b8f 100644 --- a/ee/spec/services/projects/transfer_service_spec.rb +++ b/ee/spec/services/projects/transfer_service_spec.rb @@ -274,5 +274,70 @@ def operation subject.execute(other_group) end end + + describe 'subscription seat assignments reconciliation' do + context 'seat assignment creation' do + let_it_be_with_refind(:project_2) { create(:project, namespace: group) } + + before do + stub_saas_features(gitlab_com_subscriptions: true) + end + + context 'when we transfer from user_namespace to group_namespace' do + before do + project.update!(namespace: user.namespace) + end + + it 'enqueues CreateProjectSeatsWorker with the project id' do + expect(project.namespace).to eq(user.namespace) + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateProjectSeatsWorker) + .to receive(:perform_async).with(project.id) + + subject.execute(group) + expect(project.namespace).to eq(group) + end + end + + context 'when we transfer from group_namespace to user_namespace' do + it 'enqueues CreateProjectSeatsWorker with the project id' do + expect(project_2.namespace).to eq(group) + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateProjectSeatsWorker) + .to receive(:perform_async).with(project_2.id) + + described_class.new(project_2, user).execute(user.namespace) + expect(project_2.namespace).to eq(user.namespace) + end + end + + context 'when we transfer from a group_namespace to another group_namespace' do + let(:new_group) { create(:group, :public, owners: user) } + + it 'enqueues CreateProjectSeatsWorker with the project id' do + expect(project_2.namespace).to eq(group) + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateProjectSeatsWorker) + .to receive(:perform_async).with(project_2.id) + + described_class.new(project_2, user).execute(new_group) + + expect(project_2.namespace).to eq(new_group) + end + end + + context 'when a project is transferred to a sub-group in the same hierarchy' do + let(:subgroup_to_be_new_parent) { create(:group, :private, parent: group) } + + it 'does not enqueue CreateProjectSeatsWorker' do + expect(project_2.namespace).to eq(group) + expect(project_2.namespace).to eq(subgroup_to_be_new_parent.root_ancestor) + expect(GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateProjectSeatsWorker) + .not_to receive(:perform_async) + + described_class.new(project_2, user).execute(subgroup_to_be_new_parent) + + expect(project_2.namespace.root_ancestor).to eq(group) + end + end + end + end end end